|
1 | 1 | import * as jose from "jose";
|
2 | 2 |
|
3 | 3 | import { enableLogging, logDebugMessage } from "../logger";
|
| 4 | +import { doesSessionExist, getAccessTokenPayloadSecurely } from "supertokens-web-js/recipe/session"; |
4 | 5 |
|
5 | 6 | import {
|
6 | 7 | FRONT_TOKEN_HEADER_NAME,
|
@@ -110,13 +111,46 @@ export default class SuperTokensNextjsSSRAPIWrapper {
|
110 | 111 | * @param cookies - The cookies store exposed by next/headers (await cookies())
|
111 | 112 | * @returns The session context value or undefined if the session does not exist or is invalid
|
112 | 113 | **/
|
113 |
| - static async getServerActionSession(cookies: CookiesStore): Promise<LoadedSessionContext | undefined> { |
| 114 | + static async getServerActionSession( |
| 115 | + cookies: CookiesStore |
| 116 | + ): Promise< |
| 117 | + { session: LoadedSessionContext; status: "valid" } | { status: "expired" | "invalid"; session: undefined } |
| 118 | + > { |
114 | 119 | const { state, session } = await getSSRSessionState(cookies);
|
115 | 120 | logDebugMessage(`SSR Session State: ${state}`);
|
116 | 121 | if (state === "tokens-match") {
|
117 |
| - return session; |
| 122 | + return { session: session as LoadedSessionContext, status: "valid" }; |
| 123 | + } else if (["tokens-do-not-match", "access-token-not-found", "access-token-invalid"].includes(state)) { |
| 124 | + return { status: "expired", session: undefined }; |
| 125 | + } |
| 126 | + |
| 127 | + return { status: "invalid", session: undefined }; |
| 128 | + } |
| 129 | + |
| 130 | + // TODO: How do we check for specific st errors here |
| 131 | + // The website library just throws new Error('message') |
| 132 | + // so we don't have a reliable way to differentiate between |
| 133 | + // our errors and consumer errors |
| 134 | + static async authenticateServerAction<T extends (session?: LoadedSessionContext) => Promise<K>, K>(action: T) { |
| 135 | + try { |
| 136 | + const sessionExists = await doesSessionExist(); |
| 137 | + logDebugMessage(`Session exists: ${sessionExists}`); |
| 138 | + if (!sessionExists) { |
| 139 | + logDebugMessage(`Performing action without session`); |
| 140 | + return action(); |
| 141 | + } |
| 142 | + |
| 143 | + const accessTokenPayload = await getAccessTokenPayloadSecurely(); |
| 144 | + logDebugMessage(`Retrieved access token payload`); |
| 145 | + const loadedSessionContext = buildLoadedSessionContext(accessTokenPayload); |
| 146 | + console.log("access token payload", accessTokenPayload); |
| 147 | + |
| 148 | + logDebugMessage(`Passing session context to action`); |
| 149 | + const result = await action(loadedSessionContext); |
| 150 | + return result; |
| 151 | + } catch (err) { |
| 152 | + return action(); |
118 | 153 | }
|
119 |
| - return; |
120 | 154 | }
|
121 | 155 |
|
122 | 156 | /**
|
@@ -162,6 +196,7 @@ export const init = SuperTokensNextjsSSRAPIWrapper.init;
|
162 | 196 | export const getServerComponentSession = SuperTokensNextjsSSRAPIWrapper.getServerComponentSession;
|
163 | 197 | export const getServerActionSession = SuperTokensNextjsSSRAPIWrapper.getServerActionSession;
|
164 | 198 | export const getServerSidePropsSession = SuperTokensNextjsSSRAPIWrapper.getServerSidePropsSession;
|
| 199 | +export const authenticateServerAction = SuperTokensNextjsSSRAPIWrapper.authenticateServerAction; |
165 | 200 |
|
166 | 201 | function getAuthPagePath(redirectPath: string): string {
|
167 | 202 | const authPagePath = SuperTokensNextjsSSRAPIWrapper.getConfigOrThrow().appInfo.websiteBasePath || "/auth";
|
@@ -207,13 +242,17 @@ async function getSSRSessionState(
|
207 | 242 |
|
208 | 243 | return {
|
209 | 244 | state: "tokens-match",
|
210 |
| - session: { |
211 |
| - userId: parsedAccessToken.payload.sub, |
212 |
| - accessTokenPayload: parsedAccessToken, |
213 |
| - doesSessionExist: true, |
214 |
| - loading: false, |
215 |
| - invalidClaims: [], |
216 |
| - }, |
| 245 | + session: buildLoadedSessionContext(parsedAccessToken.payload), |
| 246 | + }; |
| 247 | +} |
| 248 | + |
| 249 | +function buildLoadedSessionContext(accessTokenPayload: AccessTokenPayload["up"]): LoadedSessionContext { |
| 250 | + return { |
| 251 | + userId: accessTokenPayload.sub, |
| 252 | + accessTokenPayload, |
| 253 | + doesSessionExist: true, |
| 254 | + loading: false, |
| 255 | + invalidClaims: [], |
217 | 256 | };
|
218 | 257 | }
|
219 | 258 |
|
|
0 commit comments