diff --git a/packages/core/src/api/refreshUserInfo.ts b/packages/core/src/api/refreshUserInfo.ts index da305c2a..80d28029 100644 --- a/packages/core/src/api/refreshUserInfo.ts +++ b/packages/core/src/api/refreshUserInfo.ts @@ -91,19 +91,19 @@ export const refreshUserInfo = async ( identity: ctx.identity, }) return { - success: true, + success: !!session, headers: mergedHeaders, - session, + session: session, toResponse: () => { return Response.json( { + success: !!session, session, - success: true, }, { headers: mergedHeaders, status: 200 } ) }, - } + } as RefreshUserInfoAPIReturn } catch (error) { const { code, message, statusCode } = handleApiError( error, diff --git a/packages/core/src/shared/utils.ts b/packages/core/src/shared/utils.ts index c75316dd..20b29c62 100644 --- a/packages/core/src/shared/utils.ts +++ b/packages/core/src/shared/utils.ts @@ -231,10 +231,13 @@ export const getStandardSession = async ({ }) => { const claims = await jwt.verifyToken(sessionToken) const parsedClaims = identity.skipValidation ? claims : await identity.schemaRegistry.parseWithJWT(claims) - const { exp: _exp, iat: _iat, mexp: _mexp, ...defaultPayload } = parsedClaims + const { exp, iat: _iat, mexp: _mexp, ...defaultPayload } = parsedClaims const userClaims = await identity.schemaRegistry.parse(defaultPayload) if (!userClaims.sub) return null - return userClaims + return { + user: userClaims, + expires: exp, + } } export const transformToTokenPayload = (tokens: OAuthAccessTokenResponseType & { id_token?: string }) => { diff --git a/packages/core/test/actions/providers/user/stateless.test.ts b/packages/core/test/actions/providers/user/stateless.test.ts index ab0373e7..2ecbd669 100644 --- a/packages/core/test/actions/providers/user/stateless.test.ts +++ b/packages/core/test/actions/providers/user/stateless.test.ts @@ -90,10 +90,8 @@ describe("refreshUserInfo action", () => { ok: true, headers: new Headers({ "Content-Type": "application/json" }), json: async () => ({ - id: "1234567890", - email: "john@example.com", - name: "John Doe", - image: "https://example.com/image.jpg", + ...sessionPayload, + email: "john.updated@example.com", }), }) vi.stubGlobal("fetch", mockFetch) @@ -111,10 +109,11 @@ describe("refreshUserInfo action", () => { expect(await response.json()).toEqual({ success: true, session: { - sub: "1234567890", - email: "john@example.com", - name: "John Doe", - image: "https://example.com/image.jpg", + user: { + ...sessionPayload, + email: "john.updated@example.com", + }, + expires: expect.any(Number), }, }) expect(response.status).toBe(200) @@ -294,9 +293,8 @@ describe("refreshUserInfo action", () => { headers: new Headers({ "Content-Type": "application/json" }), json: async () => ({ id: "1234567890", - email: "john@example.com", - name: "John Doe", - image: "https://example.com/image.jpg", + ...sessionPayload, + email: "john.updated@example.com", }), }) vi.stubGlobal("fetch", mockFetch) @@ -314,10 +312,11 @@ describe("refreshUserInfo action", () => { expect(await response.json()).toEqual({ success: true, session: { - sub: "1234567890", - email: "john@example.com", - name: "John Doe", - image: "https://example.com/image.jpg", + user: { + ...sessionPayload, + email: "john.updated@example.com", + }, + expires: expect.any(Number), }, }) expect(mockFetch).toHaveBeenCalledTimes(2) @@ -388,10 +387,9 @@ describe("refreshUserInfo action", () => { ok: true, headers: new Headers({ "Content-Type": "application/json" }), json: async () => ({ + ...sessionPayload, id: "1234567890", - email: "john@example.com", - name: "John Doe", - image: "https://example.com/image.jpg", + email: "john.updated@example.com", }), }) vi.stubGlobal("fetch", mockFetch) @@ -409,10 +407,11 @@ describe("refreshUserInfo action", () => { expect(await response.json()).toEqual({ success: true, session: { - sub: "1234567890", - email: "john@example.com", - name: "John Doe", - image: "https://example.com/image.jpg", + user: { + ...sessionPayload, + email: "john.updated@example.com", + }, + expires: expect.any(Number), }, }) expect(response.headers.get("set-cookie")).toContain("aura-auth.session_token=") @@ -449,10 +448,9 @@ describe("refreshUserInfo action", () => { ok: true, headers: new Headers({ "Content-Type": "application/json" }), json: async () => ({ + ...sessionPayload, id: "1234567890", - email: "john@example.com", - name: "John Doe", - image: "https://example.com/image.jpg", + email: "john.updated@example.com", nickname: "johnny", email_verified: true, }), @@ -471,12 +469,13 @@ describe("refreshUserInfo action", () => { expect(await response.json()).toEqual({ success: true, - session: expect.objectContaining({ - sub: "1234567890", - email: "john@example.com", - name: "John Doe", - image: "https://example.com/image.jpg", - }), + session: { + user: { + ...sessionPayload, + email: "john.updated@example.com", + }, + expires: expect.any(Number), + }, }) }) @@ -491,10 +490,8 @@ describe("refreshUserInfo action", () => { ok: true, headers: new Headers({ "Content-Type": "application/json" }), json: async () => ({ - id: "1234567890", - email: "john@example.com", - name: "John Doe", - image: "https://example.com/image.jpg", + ...sessionPayload, + email: "john.updated@example.com", }), }) vi.stubGlobal("fetch", mockFetch) @@ -512,10 +509,11 @@ describe("refreshUserInfo action", () => { expect(await response.json()).toEqual({ success: true, session: { - sub: "1234567890", - email: "john@example.com", - name: "John Doe", - image: "https://example.com/image.jpg", + user: { + ...sessionPayload, + email: "john.updated@example.com", + }, + expires: expect.any(Number), }, }) }) diff --git a/packages/core/test/api/stateless/getSession.test.ts b/packages/core/test/api/stateless/getSession.test.ts index 6982f821..81038421 100644 --- a/packages/core/test/api/stateless/getSession.test.ts +++ b/packages/core/test/api/stateless/getSession.test.ts @@ -1,7 +1,7 @@ -import { getCookie, getSetCookie } from "@/cookie.ts" -import { createAuth } from "@/createAuth.ts" -import { api, jose } from "@test/presets.ts" import { describe, test, expect } from "vitest" +import { createAuth } from "@/createAuth.ts" +import { getCookie, getSetCookie } from "@/cookie.ts" +import { api, jose, sessionPayload } from "@test/presets.ts" describe("getSession", () => { test("getSession with no session token", async () => { @@ -25,95 +25,91 @@ describe("getSession", () => { }) test("getSession with valid session token", async () => { - const jwt = await jose.encodeJWT({ - sub: "123", - name: "Alice", - email: "alice@example.com", - }) + const jwt = await jose.encodeJWT(sessionPayload) const session = await api.getSession({ headers: { Cookie: `aura-auth.session_token=${jwt}` }, }) - expect(session.session).toMatchObject({ - user: { - sub: "123", - name: "Alice", - email: "alice@example.com", + expect(session).toEqual({ + success: true, + session: { + user: sessionPayload, + expires: expect.any(String), }, - expires: expect.any(String), + headers: expect.any(Headers), + toResponse: expect.any(Function), }) }) test("getSession with expired session token", async () => { const jwt = await jose.encodeJWT({ - sub: "123", - name: "Alice", - email: "", + ...sessionPayload, exp: Math.floor(Date.now() / 1000) - 60, }) const session = await api.getSession({ headers: { Cookie: `aura-auth.session_token=${jwt}` }, }) - expect(session).toMatchObject({ + expect(session).toEqual({ session: null, - headers: {}, success: false, + error: { + code: "GET_SESSION_FAILED", + message: "Failed to retrieve session. The session token may be missing, expired, or invalid.", + }, + headers: expect.any(Headers), + toResponse: expect.any(Function), }) expect(getSetCookie(session.headers, "aura-auth.session_token")).toBe("") }) test("getSession with session token missing sub claim", async () => { - const jwt = await jose.encodeJWT({ - name: "Alice", - email: "alice@example.com", - }) + const { sub: _, ...spreadSession } = sessionPayload + const jwt = await jose.encodeJWT(spreadSession) const session = await api.getSession({ headers: { Cookie: `aura-auth.session_token=${jwt}` }, }) - expect(session).toMatchObject({ + expect(session).toEqual({ session: null, - headers: {}, success: false, + error: { + code: "GET_SESSION_FAILED", + message: "Failed to retrieve session. The session token may be missing, expired, or invalid.", + }, + headers: expect.any(Headers), + toResponse: expect.any(Function), }) }) test("getSession with extra claims in session token", async () => { const jwt = await jose.encodeJWT({ - sub: "123", - name: "Alice", - email: "alice@example.com", + ...sessionPayload, role: "admin", permissions: ["read", "write"], }) const session = await api.getSession({ headers: { Cookie: `aura-auth.session_token=${jwt}` }, }) - expect(session.session).toMatchObject({ - user: { - sub: "123", - name: "Alice", - email: "alice@example.com", + expect(session).toEqual({ + success: true, + session: { + user: sessionPayload, + expires: expect.any(String), }, - expires: expect.any(String), + headers: expect.any(Headers), + toResponse: expect.any(Function), }) - expect(session.session?.user).not.toHaveProperty("role") - expect(session.session?.user).not.toHaveProperty("permissions") - const decodeSession = await jose.decodeJWT(getCookie(session.headers, "aura-auth.session_token")!) - expect(decodeSession).toMatchObject({ - sub: "123", - name: "Alice", - email: "alice@example.com", + expect(session.session).not.toContainEqual({ + role: "admin", + permissions: ["read", "write"], }) - expect(session.session?.user).not.toHaveProperty("role") - expect(session.session?.user).not.toHaveProperty("permissions") + const decodeSession = await jose.decodeJWT(getCookie(session.headers, "aura-auth.session_token")!) + expect(decodeSession).toMatchObject(sessionPayload) }) test("getSession refreshes session token if exp is close", async () => { const auth = createAuth({ oauth: [], session: { jwt: { expirationStrategy: "rolling" } } }) const jwt = await auth.jose.encodeJWT({ - sub: "123", - name: "Alice", - email: "alice@example.com", + ...sessionPayload, iat: Math.floor(Date.now() / 1000) - 3600, exp: Math.floor(Date.now() / 1000) + 10, role: "admin", @@ -122,23 +118,21 @@ describe("getSession", () => { const session = await auth.api.getSession({ headers: { Cookie: `aura-auth.session_token=${jwt}` }, }) - expect(session.session).toMatchObject({ - user: { - sub: "123", - name: "Alice", - email: "alice@example.com", + expect(session).toEqual({ + success: true, + session: { + user: sessionPayload, + expires: expect.any(String), }, + headers: expect.any(Headers), + toResponse: expect.any(Function), + }) + expect(session.session).not.toContainEqual({ + role: "admin", + permissions: ["read", "write"], }) - expect(session.session?.user).not.toHaveProperty("role") - expect(session.session?.user).not.toHaveProperty("permissions") const decodeSession = await jose.decodeJWT(getSetCookie(session.headers, "aura-auth.session_token")!) - expect(decodeSession).toMatchObject({ - sub: "123", - name: "Alice", - email: "alice@example.com", - }) - expect(session.session?.user).not.toHaveProperty("role") - expect(session.session?.user).not.toHaveProperty("permissions") + expect(decodeSession).toMatchObject(sessionPayload) }) }) diff --git a/packages/core/test/api/stateless/refreshUserInfo.test.ts b/packages/core/test/api/stateless/refreshUserInfo.test.ts index 23bd28fa..eabca137 100644 --- a/packages/core/test/api/stateless/refreshUserInfo.test.ts +++ b/packages/core/test/api/stateless/refreshUserInfo.test.ts @@ -91,10 +91,8 @@ describe("refreshUserInfo", () => { ok: true, headers: new Headers({ "Content-Type": "application/json" }), json: async () => ({ - id: "1234567890", - email: "john@example.com", - name: "John Doe", - image: "https://example.com/image.jpg", + ...sessionPayload, + email: "john.updated@example.com", }), }) vi.stubGlobal("fetch", mockFetch) @@ -109,10 +107,11 @@ describe("refreshUserInfo", () => { expect(output).toEqual({ success: true, session: { - sub: "1234567890", - email: "john@example.com", - name: "John Doe", - image: "https://example.com/image.jpg", + user: { + ...sessionPayload, + email: "john.updated@example.com", + }, + expires: expect.any(Number), }, headers: expect.any(Headers), toResponse: expect.any(Function), @@ -324,10 +323,9 @@ describe("refreshUserInfo", () => { ok: true, headers: new Headers({ "Content-Type": "application/json" }), json: async () => ({ + ...sessionPayload, id: "1234567890", - email: "john@example.com", - name: "John Doe", - image: "https://example.com/image.jpg", + email: "john.updated@example.com", }), }) vi.stubGlobal("fetch", mockFetch) @@ -339,8 +337,18 @@ describe("refreshUserInfo", () => { }, }) - expect(output.success).toBe(true) - expect(output.session).not.toBeNull() + expect(output).toEqual({ + success: true, + session: { + user: { + ...sessionPayload, + email: "john.updated@example.com", + }, + expires: expect.any(Number), + }, + headers: expect.any(Headers), + toResponse: expect.any(Function), + }) expect(mockFetch).toHaveBeenCalledTimes(2) }) @@ -419,10 +427,8 @@ describe("refreshUserInfo", () => { ok: true, headers: new Headers({ "Content-Type": "application/json" }), json: async () => ({ - id: "1234567890", - email: "john@example.com", - name: "John Doe", - image: "https://example.com/image.jpg", + ...sessionPayload, + email: "john.updated@example.com", }), }) vi.stubGlobal("fetch", mockFetch) @@ -434,7 +440,18 @@ describe("refreshUserInfo", () => { }, }) - expect(output.success).toBe(true) + expect(output).toEqual({ + success: true, + session: { + user: { + ...sessionPayload, + email: "john.updated@example.com", + }, + expires: expect.any(Number), + }, + headers: expect.any(Headers), + toResponse: expect.any(Function), + }) const setCookieHeader = output.headers.get("set-cookie") expect(setCookieHeader).toContain("aura-auth.session_token=") }) @@ -476,9 +493,7 @@ describe("refreshUserInfo", () => { headers: new Headers({ "Content-Type": "application/json" }), json: async () => ({ id: "1234567890", - email: "john@example.com", - name: "John Doe", - image: "https://example.com/image.jpg", + ...sessionPayload, nickname: "johnny", email_verified: true, }), @@ -495,10 +510,8 @@ describe("refreshUserInfo", () => { expect(output).toEqual({ success: true, session: { - sub: "1234567890", - email: "john@example.com", - name: "John Doe", - image: "https://example.com/image.jpg", + user: sessionPayload, + expires: expect.any(Number), }, headers: expect.any(Headers), toResponse: expect.any(Function), @@ -517,10 +530,8 @@ describe("refreshUserInfo", () => { ok: true, headers: new Headers({ "Content-Type": "application/json" }), json: async () => ({ - id: "1234567890", - email: "john@example.com", - name: "John Doe", - image: "https://example.com/image.jpg", + ...sessionPayload, + email: "john.updated@example.com", }), }) vi.stubGlobal("fetch", mockFetch) @@ -538,10 +549,11 @@ describe("refreshUserInfo", () => { expect(await response.json()).toEqual({ success: true, session: { - sub: "1234567890", - email: "john@example.com", - name: "John Doe", - image: "https://example.com/image.jpg", + user: { + ...sessionPayload, + email: "john.updated@example.com", + }, + expires: expect.any(Number), }, }) }) @@ -558,10 +570,8 @@ describe("refreshUserInfo", () => { ok: true, headers: new Headers({ "Content-Type": "application/json" }), json: async () => ({ - id: "1234567890", - email: "john@example.com", - name: "John Doe", - image: "https://example.com/image.jpg", + ...sessionPayload, + email: "john.updated@example.com", }), }) vi.stubGlobal("fetch", mockFetch) @@ -576,10 +586,11 @@ describe("refreshUserInfo", () => { expect(output).toEqual({ success: true, session: { - sub: "1234567890", - email: "john@example.com", - name: "John Doe", - image: "https://example.com/image.jpg", + user: { + ...sessionPayload, + email: "john.updated@example.com", + }, + expires: expect.any(Number), }, headers: expect.any(Headers), toResponse: expect.any(Function), @@ -607,10 +618,8 @@ describe("refreshUserInfo", () => { ok: true, headers: new Headers({ "Content-Type": "application/json" }), json: async () => ({ - id: "1234567890", - email: "john@example.com", - name: "John Doe", - image: "https://example.com/image.jpg", + ...sessionPayload, + email: "john.updated@example.com", }), }) vi.stubGlobal("fetch", mockFetch) @@ -647,10 +656,8 @@ describe("refreshUserInfo", () => { ok: true, headers: new Headers({ "Content-Type": "application/json" }), json: async () => ({ - id: "1234567890", - email: "john@example.com", - name: "John Doe", - image: "https://example.com/image.jpg", + ...sessionPayload, + email: "john.updated@example.com", }), }) vi.stubGlobal("fetch", mockFetch) @@ -666,10 +673,11 @@ describe("refreshUserInfo", () => { expect(output).toEqual({ success: true, session: { - sub: "1234567890", - email: "john@example.com", - name: "John Doe", - image: "https://example.com/image.jpg", + user: { + ...sessionPayload, + email: "john.updated@example.com", + }, + expires: expect.any(Number), }, headers: expect.any(Headers), toResponse: expect.any(Function), @@ -697,10 +705,8 @@ describe("refreshUserInfo", () => { ok: true, headers: new Headers({ "Content-Type": "application/json" }), json: async () => ({ - id: "1234567890", - email: "john@example.com", - name: "John Doe", - image: "https://example.com/image.jpg", + ...sessionPayload, + email: "john.updated@example.com", }), }) vi.stubGlobal("fetch", mockFetch)