@authgear/nextjs 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -4
- package/dist/{chunk-HYKCRZLJ.js → chunk-AJJAXXPI.js} +7 -1
- package/dist/chunk-AJJAXXPI.js.map +1 -0
- package/dist/{chunk-PUKK75RO.js → chunk-GXYXHQVJ.js} +4 -1
- package/dist/chunk-GXYXHQVJ.js.map +1 -0
- package/dist/{chunk-A3E57VNZ.js → chunk-LPGVCNZ6.js} +4 -3
- package/dist/chunk-LPGVCNZ6.js.map +1 -0
- package/dist/client.d.ts +23 -4
- package/dist/client.js +44 -7
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +58 -7
- package/dist/index.js.map +1 -1
- package/dist/proxy.d.ts +1 -1
- package/dist/proxy.js +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/server.js +5 -3
- package/dist/server.js.map +1 -1
- package/dist/{types-BUoN9wBp.d.ts → types-D6m4Hact.d.ts} +26 -2
- package/package.json +1 -1
- package/dist/chunk-A3E57VNZ.js.map +0 -1
- package/dist/chunk-HYKCRZLJ.js.map +0 -1
- package/dist/chunk-PUKK75RO.js.map +0 -1
package/README.md
CHANGED
|
@@ -199,9 +199,10 @@ export async function callMyApiAction() {
|
|
|
199
199
|
|
|
200
200
|
### `@authgear/nextjs`
|
|
201
201
|
|
|
202
|
-
| Export | Description |
|
|
203
|
-
|
|
204
|
-
| `createAuthgearHandlers(config)` | Returns `{ GET, POST }` for `app/api/auth/[...authgear]/route.ts` |
|
|
202
|
+
| Export | Kind | Values | Description |
|
|
203
|
+
|---|---|---|---|
|
|
204
|
+
| `createAuthgearHandlers(config)` | Function | — | Returns `{ GET, POST }` for `app/api/auth/[...authgear]/route.ts` |
|
|
205
|
+
| `PromptOption` | Enum | `"login"` \| `"none"` | Convenience constants for the OIDC `prompt` parameter. Pass to `signIn({ prompt: PromptOption.Login })` to force the login form for a specific sign-in call. |
|
|
205
206
|
|
|
206
207
|
### `@authgear/nextjs/server`
|
|
207
208
|
|
|
@@ -217,10 +218,27 @@ export async function callMyApiAction() {
|
|
|
217
218
|
| Export | Description |
|
|
218
219
|
|---|---|
|
|
219
220
|
| `<AuthgearProvider>` | React context provider, must wrap the app |
|
|
220
|
-
| `useAuthgear()` | Returns `{ state, user, isLoaded, isAuthenticated, signIn, signOut }` |
|
|
221
|
+
| `useAuthgear()` | Returns `{ state, user, isLoaded, isAuthenticated, signIn, signOut, openPage }` |
|
|
221
222
|
| `useUser()` | Returns `UserInfo \| null` |
|
|
222
223
|
| `<SignInButton>` | Button that calls `signIn()` on click |
|
|
223
224
|
| `<SignOutButton>` | Button that calls `signOut()` on click |
|
|
225
|
+
| `<UserSettingsButton>` | Button that opens Authgear account settings in a new tab |
|
|
226
|
+
|
|
227
|
+
**`AuthgearProvider` props**:
|
|
228
|
+
|
|
229
|
+
| Prop | Default | Description |
|
|
230
|
+
|---|---|---|
|
|
231
|
+
| `openPagePath` | `"/api/auth/open"` | Route used by `openPage()` and `<UserSettingsButton>` to pre-authenticate and redirect to an Authgear page |
|
|
232
|
+
|
|
233
|
+
### `SignInOptions`
|
|
234
|
+
|
|
235
|
+
Options accepted by `signIn()` (from `useAuthgear()`) and the `signInOptions` prop on `<SignInButton>`.
|
|
236
|
+
|
|
237
|
+
| Field | Type | Required | Default | Description |
|
|
238
|
+
|---|---|---|---|---|
|
|
239
|
+
| `returnTo` | `string` | No | — | Path to redirect to after sign-in. |
|
|
240
|
+
| `loginPath` | `string` | No | `"/api/auth/login"` | Override the login route for this call. |
|
|
241
|
+
| `prompt` | `string` | No | — | OIDC prompt value for this sign-in call. Use `PromptOption.Login` to force the login form. Overrides the global `isSSOEnabled` setting. |
|
|
224
242
|
|
|
225
243
|
### `@authgear/nextjs/proxy`
|
|
226
244
|
|
|
@@ -246,6 +264,7 @@ export async function callMyApiAction() {
|
|
|
246
264
|
| `sessionSecret` | ✓ | Secret for encrypting session cookie (min 32 chars) |
|
|
247
265
|
| `postLogoutRedirectURI` | | Where to redirect after logout. Defaults to `"/"` |
|
|
248
266
|
| `scopes` | | OAuth scopes. Defaults to `["openid", "offline_access", "https://authgear.com/scopes/full-userinfo"]` |
|
|
267
|
+
| `isSSOEnabled` | | When `false`, always shows the Authgear login form (`prompt=login`), even if the user has an existing Authgear session. Recommended for single-app deployments. Defaults to `true` |
|
|
249
268
|
| `cookieName` | | Session cookie name. Defaults to `"authgear.session"` |
|
|
250
269
|
|
|
251
270
|
---
|
|
@@ -3,6 +3,11 @@ var Page = /* @__PURE__ */ ((Page2) => {
|
|
|
3
3
|
Page2["Settings"] = "/settings";
|
|
4
4
|
return Page2;
|
|
5
5
|
})(Page || {});
|
|
6
|
+
var PromptOption = /* @__PURE__ */ ((PromptOption2) => {
|
|
7
|
+
PromptOption2["Login"] = "login";
|
|
8
|
+
PromptOption2["None"] = "none";
|
|
9
|
+
return PromptOption2;
|
|
10
|
+
})(PromptOption || {});
|
|
6
11
|
var DEFAULT_SCOPES = [
|
|
7
12
|
"openid",
|
|
8
13
|
"offline_access",
|
|
@@ -17,7 +22,8 @@ var SessionState = /* @__PURE__ */ ((SessionState2) => {
|
|
|
17
22
|
|
|
18
23
|
export {
|
|
19
24
|
Page,
|
|
25
|
+
PromptOption,
|
|
20
26
|
DEFAULT_SCOPES,
|
|
21
27
|
SessionState
|
|
22
28
|
};
|
|
23
|
-
//# sourceMappingURL=chunk-
|
|
29
|
+
//# sourceMappingURL=chunk-AJJAXXPI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["export interface AuthgearConfig {\n /** Authgear endpoint, e.g. \"https://myapp.authgear.cloud\" */\n endpoint: string;\n /** OAuth client ID */\n clientID: string;\n /** Redirect URI for OAuth callback, e.g. \"http://localhost:3000/api/auth/callback\" */\n redirectURI: string;\n /** Where to redirect after logout */\n postLogoutRedirectURI?: string;\n /** OAuth scopes. Defaults to [\"openid\", \"offline_access\", \"https://authgear.com/scopes/full-userinfo\"] */\n scopes?: string[];\n /** Secret key for encrypting session cookie (min 32 chars) */\n sessionSecret: string;\n /** Session cookie name. Defaults to \"authgear.session\" */\n cookieName?: string;\n /**\n * Whether to enable SSO (Single Sign-On) with other apps on the same Authgear tenant.\n * When `true` (default), Authgear silently reuses its server-side session if the user\n * is already logged in, so users are not prompted for credentials again.\n * Set to `false` to always show the login form (`prompt=login`), which is recommended\n * for single-app deployments where silent sign-in feels unexpected to the user.\n * Defaults to `true`.\n */\n isSSOEnabled?: boolean;\n}\n\n/**\n * Pages that can be opened in a new tab with the current user pre-authenticated.\n * Used by `getOpenURL` from `@authgear/nextjs/server` and by `openPage` / `<UserSettingsButton>`\n * from `@authgear/nextjs/client`.\n */\nexport enum Page {\n Settings = \"/settings\",\n}\n\n/**\n * OIDC `prompt` parameter values.\n * Pass to `signIn({ prompt })` or `SignInButton signInOptions={{ prompt }}` to control\n * whether Authgear shows the login form for a specific authentication call.\n *\n * @see https://docs.authgear.com/authentication-and-access/single-sign-on/force-authgear-to-show-login-page\n */\nexport enum PromptOption {\n /** Always show the login form, even if the user has an active Authgear session. */\n Login = \"login\",\n /** Never show the login form; return an error if the user is not already authenticated. */\n None = \"none\",\n}\n\nexport const DEFAULT_SCOPES = [\n \"openid\",\n \"offline_access\",\n \"https://authgear.com/scopes/full-userinfo\",\n];\n\nexport enum SessionState {\n Unknown = \"UNKNOWN\",\n NoSession = \"NO_SESSION\",\n Authenticated = \"AUTHENTICATED\",\n}\n\nexport interface SessionData {\n accessToken: string;\n refreshToken: string | null;\n idToken: string | null;\n expiresAt: number;\n}\n\nexport interface Session {\n state: SessionState;\n accessToken: string | null;\n refreshToken: string | null;\n idToken: string | null;\n expiresAt: number | null;\n user: UserInfo | null;\n}\n\nexport interface UserInfo {\n sub: string;\n email?: string;\n emailVerified?: boolean;\n phoneNumber?: string;\n phoneNumberVerified?: boolean;\n preferredUsername?: string;\n givenName?: string;\n familyName?: string;\n name?: string;\n picture?: string;\n roles?: string[];\n isAnonymous?: boolean;\n isVerified?: boolean;\n canReauthenticate?: boolean;\n customAttributes?: Record<string, unknown>;\n raw: Record<string, unknown>;\n}\n\nexport interface JWTPayload {\n sub: string;\n iss: string;\n aud: string | string[];\n exp: number;\n iat: number;\n jti?: string;\n client_id?: string;\n \"https://authgear.com/claims/user/is_anonymous\"?: boolean;\n \"https://authgear.com/claims/user/is_verified\"?: boolean;\n \"https://authgear.com/claims/user/can_reauthenticate\"?: boolean;\n \"https://authgear.com/claims/user/roles\"?: string[];\n [key: string]: unknown;\n}\n\nexport interface TokenResponse {\n access_token: string;\n token_type: string;\n expires_in: number;\n refresh_token?: string;\n id_token?: string;\n}\n\nexport interface AppSessionTokenResponse {\n app_session_token: string;\n expire_at: string;\n}\n\nexport interface OIDCConfiguration {\n authorization_endpoint: string;\n token_endpoint: string;\n userinfo_endpoint: string;\n revocation_endpoint: string;\n end_session_endpoint: string;\n jwks_uri: string;\n issuer: string;\n}\n"],"mappings":";AA+BO,IAAK,OAAL,kBAAKA,UAAL;AACL,EAAAA,MAAA,cAAW;AADD,SAAAA;AAAA,GAAA;AAWL,IAAK,eAAL,kBAAKC,kBAAL;AAEL,EAAAA,cAAA,WAAQ;AAER,EAAAA,cAAA,UAAO;AAJG,SAAAA;AAAA,GAAA;AAOL,IAAM,iBAAiB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAK,eAAL,kBAAKC,kBAAL;AACL,EAAAA,cAAA,aAAU;AACV,EAAAA,cAAA,eAAY;AACZ,EAAAA,cAAA,mBAAgB;AAHN,SAAAA;AAAA,GAAA;","names":["Page","PromptOption","SessionState"]}
|
|
@@ -37,6 +37,9 @@ function buildAuthorizeURL(oidcConfig, params) {
|
|
|
37
37
|
url.searchParams.set("code_challenge", computeCodeChallenge(params.codeVerifier));
|
|
38
38
|
url.searchParams.set("code_challenge_method", "S256");
|
|
39
39
|
url.searchParams.set("state", params.state);
|
|
40
|
+
if (params.prompt) {
|
|
41
|
+
url.searchParams.set("prompt", params.prompt);
|
|
42
|
+
}
|
|
40
43
|
return url.toString();
|
|
41
44
|
}
|
|
42
45
|
|
|
@@ -69,4 +72,4 @@ export {
|
|
|
69
72
|
buildAuthorizeURL,
|
|
70
73
|
parseUserInfo
|
|
71
74
|
};
|
|
72
|
-
//# sourceMappingURL=chunk-
|
|
75
|
+
//# sourceMappingURL=chunk-GXYXHQVJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/oauth/authorize.ts","../src/oauth/pkce.ts","../src/user.ts"],"sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport type { OIDCConfiguration } from \"../types.js\";\nimport { computeCodeChallenge } from \"./pkce.js\";\n\n/**\n * Build the URL to open an Authgear page (e.g. /settings) with the user\n * already authenticated via an app session token.\n */\nexport function buildOpenURL(\n oidcConfig: OIDCConfiguration,\n params: {\n clientID: string;\n appSessionToken: string;\n targetPath: string; // e.g. \"/settings\"\n scopes: string[];\n },\n): string {\n const authorizationEndpoint = new URL(oidcConfig.authorization_endpoint);\n const settingsURL = `${authorizationEndpoint.origin}${params.targetPath}`;\n const loginHint = `https://authgear.com/login_hint?type=app_session_token&app_session_token=${encodeURIComponent(params.appSessionToken)}`;\n\n const url = new URL(oidcConfig.authorization_endpoint);\n url.searchParams.set(\"response_type\", \"none\");\n url.searchParams.set(\"client_id\", params.clientID);\n url.searchParams.set(\"redirect_uri\", settingsURL);\n url.searchParams.set(\"scope\", params.scopes.join(\" \"));\n url.searchParams.set(\"prompt\", \"none\");\n url.searchParams.set(\"login_hint\", loginHint);\n return url.toString();\n}\n\nexport interface AuthorizeParams {\n clientID: string;\n redirectURI: string;\n scopes: string[];\n codeVerifier: string;\n state: string;\n prompt?: string;\n}\n\nexport function generateState(): string {\n return randomBytes(32).toString(\"base64url\");\n}\n\nexport function buildAuthorizeURL(\n oidcConfig: OIDCConfiguration,\n params: AuthorizeParams,\n): string {\n const url = new URL(oidcConfig.authorization_endpoint);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"client_id\", params.clientID);\n url.searchParams.set(\"redirect_uri\", params.redirectURI);\n url.searchParams.set(\"scope\", params.scopes.join(\" \"));\n url.searchParams.set(\"code_challenge\", computeCodeChallenge(params.codeVerifier));\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n url.searchParams.set(\"state\", params.state);\n if (params.prompt) {\n url.searchParams.set(\"prompt\", params.prompt);\n }\n return url.toString();\n}\n","import { randomBytes, createHash } from \"node:crypto\";\n\nconst VERIFIER_LENGTH = 64;\n\nexport function generateCodeVerifier(): string {\n return randomBytes(VERIFIER_LENGTH)\n .toString(\"base64url\")\n .slice(0, VERIFIER_LENGTH);\n}\n\nexport function computeCodeChallenge(codeVerifier: string): string {\n return createHash(\"sha256\").update(codeVerifier).digest(\"base64url\");\n}\n","import type { UserInfo } from \"./types.js\";\n\nexport function parseUserInfo(raw: Record<string, unknown>): UserInfo {\n return {\n sub: raw[\"sub\"] as string,\n email: raw[\"email\"] as string | undefined,\n emailVerified: raw[\"email_verified\"] as boolean | undefined,\n phoneNumber: raw[\"phone_number\"] as string | undefined,\n phoneNumberVerified: raw[\"phone_number_verified\"] as boolean | undefined,\n preferredUsername: raw[\"preferred_username\"] as string | undefined,\n givenName: raw[\"given_name\"] as string | undefined,\n familyName: raw[\"family_name\"] as string | undefined,\n name: raw[\"name\"] as string | undefined,\n picture: raw[\"picture\"] as string | undefined,\n roles: raw[\"https://authgear.com/claims/user/roles\"] as string[] | undefined,\n isAnonymous: raw[\"https://authgear.com/claims/user/is_anonymous\"] as boolean | undefined,\n isVerified: raw[\"https://authgear.com/claims/user/is_verified\"] as boolean | undefined,\n canReauthenticate: raw[\"https://authgear.com/claims/user/can_reauthenticate\"] as boolean | undefined,\n customAttributes: raw[\"custom_attributes\"] as Record<string, unknown> | undefined,\n raw,\n };\n}\n"],"mappings":";AAAA,SAAS,eAAAA,oBAAmB;;;ACA5B,SAAS,aAAa,kBAAkB;AAExC,IAAM,kBAAkB;AAEjB,SAAS,uBAA+B;AAC7C,SAAO,YAAY,eAAe,EAC/B,SAAS,WAAW,EACpB,MAAM,GAAG,eAAe;AAC7B;AAEO,SAAS,qBAAqB,cAA8B;AACjE,SAAO,WAAW,QAAQ,EAAE,OAAO,YAAY,EAAE,OAAO,WAAW;AACrE;;;ADJO,SAAS,aACd,YACA,QAMQ;AACR,QAAM,wBAAwB,IAAI,IAAI,WAAW,sBAAsB;AACvE,QAAM,cAAc,GAAG,sBAAsB,MAAM,GAAG,OAAO,UAAU;AACvE,QAAM,YAAY,4EAA4E,mBAAmB,OAAO,eAAe,CAAC;AAExI,QAAM,MAAM,IAAI,IAAI,WAAW,sBAAsB;AACrD,MAAI,aAAa,IAAI,iBAAiB,MAAM;AAC5C,MAAI,aAAa,IAAI,aAAa,OAAO,QAAQ;AACjD,MAAI,aAAa,IAAI,gBAAgB,WAAW;AAChD,MAAI,aAAa,IAAI,SAAS,OAAO,OAAO,KAAK,GAAG,CAAC;AACrD,MAAI,aAAa,IAAI,UAAU,MAAM;AACrC,MAAI,aAAa,IAAI,cAAc,SAAS;AAC5C,SAAO,IAAI,SAAS;AACtB;AAWO,SAAS,gBAAwB;AACtC,SAAOC,aAAY,EAAE,EAAE,SAAS,WAAW;AAC7C;AAEO,SAAS,kBACd,YACA,QACQ;AACR,QAAM,MAAM,IAAI,IAAI,WAAW,sBAAsB;AACrD,MAAI,aAAa,IAAI,iBAAiB,MAAM;AAC5C,MAAI,aAAa,IAAI,aAAa,OAAO,QAAQ;AACjD,MAAI,aAAa,IAAI,gBAAgB,OAAO,WAAW;AACvD,MAAI,aAAa,IAAI,SAAS,OAAO,OAAO,KAAK,GAAG,CAAC;AACrD,MAAI,aAAa,IAAI,kBAAkB,qBAAqB,OAAO,YAAY,CAAC;AAChF,MAAI,aAAa,IAAI,yBAAyB,MAAM;AACpD,MAAI,aAAa,IAAI,SAAS,OAAO,KAAK;AAC1C,MAAI,OAAO,QAAQ;AACjB,QAAI,aAAa,IAAI,UAAU,OAAO,MAAM;AAAA,EAC9C;AACA,SAAO,IAAI,SAAS;AACtB;;;AE1DO,SAAS,cAAc,KAAwC;AACpE,SAAO;AAAA,IACL,KAAK,IAAI,KAAK;AAAA,IACd,OAAO,IAAI,OAAO;AAAA,IAClB,eAAe,IAAI,gBAAgB;AAAA,IACnC,aAAa,IAAI,cAAc;AAAA,IAC/B,qBAAqB,IAAI,uBAAuB;AAAA,IAChD,mBAAmB,IAAI,oBAAoB;AAAA,IAC3C,WAAW,IAAI,YAAY;AAAA,IAC3B,YAAY,IAAI,aAAa;AAAA,IAC7B,MAAM,IAAI,MAAM;AAAA,IAChB,SAAS,IAAI,SAAS;AAAA,IACtB,OAAO,IAAI,wCAAwC;AAAA,IACnD,aAAa,IAAI,+CAA+C;AAAA,IAChE,YAAY,IAAI,8CAA8C;AAAA,IAC9D,mBAAmB,IAAI,qDAAqD;AAAA,IAC5E,kBAAkB,IAAI,mBAAmB;AAAA,IACzC;AAAA,EACF;AACF;","names":["randomBytes","randomBytes"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEFAULT_SCOPES
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-AJJAXXPI.js";
|
|
4
4
|
|
|
5
5
|
// src/config.ts
|
|
6
6
|
function resolveConfig(config) {
|
|
@@ -17,7 +17,8 @@ function resolveConfig(config) {
|
|
|
17
17
|
postLogoutRedirectURI: config.postLogoutRedirectURI ?? "/",
|
|
18
18
|
scopes: config.scopes ?? DEFAULT_SCOPES,
|
|
19
19
|
sessionSecret: config.sessionSecret,
|
|
20
|
-
cookieName: config.cookieName ?? "authgear.session"
|
|
20
|
+
cookieName: config.cookieName ?? "authgear.session",
|
|
21
|
+
isSSOEnabled: config.isSSOEnabled ?? true
|
|
21
22
|
};
|
|
22
23
|
}
|
|
23
24
|
|
|
@@ -231,4 +232,4 @@ export {
|
|
|
231
232
|
deriveSessionState,
|
|
232
233
|
isTokenExpired
|
|
233
234
|
};
|
|
234
|
-
//# sourceMappingURL=chunk-
|
|
235
|
+
//# sourceMappingURL=chunk-LPGVCNZ6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config.ts","../src/oauth/discovery.ts","../src/session/cookie.ts","../src/oauth/token.ts","../src/session/state.ts"],"sourcesContent":["import { type AuthgearConfig, DEFAULT_SCOPES } from \"./types.js\";\n\nexport function resolveConfig(config: AuthgearConfig): Required<AuthgearConfig> {\n if (!config.endpoint) throw new Error(\"AuthgearConfig: endpoint is required\");\n if (!config.clientID) throw new Error(\"AuthgearConfig: clientID is required\");\n if (!config.redirectURI) throw new Error(\"AuthgearConfig: redirectURI is required\");\n if (!config.sessionSecret || config.sessionSecret.length < 32) {\n throw new Error(\"AuthgearConfig: sessionSecret must be at least 32 characters\");\n }\n\n return {\n endpoint: config.endpoint.replace(/\\/+$/, \"\"),\n clientID: config.clientID,\n redirectURI: config.redirectURI,\n postLogoutRedirectURI: config.postLogoutRedirectURI ?? \"/\",\n scopes: config.scopes ?? DEFAULT_SCOPES,\n sessionSecret: config.sessionSecret,\n cookieName: config.cookieName ?? \"authgear.session\",\n isSSOEnabled: config.isSSOEnabled ?? true,\n };\n}\n","import type { OIDCConfiguration } from \"../types.js\";\n\nconst cache = new Map<string, { config: OIDCConfiguration; fetchedAt: number }>();\nconst CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour\n\nexport async function fetchOIDCConfiguration(\n endpoint: string,\n): Promise<OIDCConfiguration> {\n const cached = cache.get(endpoint);\n if (cached && Date.now() - cached.fetchedAt < CACHE_TTL_MS) {\n return cached.config;\n }\n\n const url = `${endpoint}/.well-known/openid-configuration`;\n const res = await fetch(url);\n if (!res.ok) {\n throw new Error(`Failed to fetch OIDC configuration from ${url}: ${res.status}`);\n }\n\n const config = (await res.json()) as OIDCConfiguration;\n cache.set(endpoint, { config, fetchedAt: Date.now() });\n return config;\n}\n\n/** Clear cached OIDC configuration (useful for testing) */\nexport function clearOIDCCache(): void {\n cache.clear();\n}\n","import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from \"node:crypto\";\nimport type { SessionData } from \"../types.js\";\n\nconst ALGORITHM = \"aes-256-gcm\";\nconst IV_LENGTH = 12;\nconst AUTH_TAG_LENGTH = 16;\nconst KEY_LENGTH = 32;\nconst SALT = \"authgear-nextjs-session\";\n\nfunction deriveKey(secret: string): Buffer {\n return scryptSync(secret, SALT, KEY_LENGTH);\n}\n\nexport function encryptSession(data: SessionData, secret: string): string {\n const key = deriveKey(secret);\n const iv = randomBytes(IV_LENGTH);\n const cipher = createCipheriv(ALGORITHM, key, iv, { authTagLength: AUTH_TAG_LENGTH });\n\n const json = JSON.stringify(data);\n const encrypted = Buffer.concat([cipher.update(json, \"utf8\"), cipher.final()]);\n const authTag = cipher.getAuthTag();\n\n // Format: base64(iv + authTag + encrypted)\n return Buffer.concat([iv, authTag, encrypted]).toString(\"base64url\");\n}\n\nexport function decryptSession(encrypted: string, secret: string): SessionData | null {\n try {\n const key = deriveKey(secret);\n const buf = Buffer.from(encrypted, \"base64url\");\n\n if (buf.length < IV_LENGTH + AUTH_TAG_LENGTH) return null;\n\n const iv = buf.subarray(0, IV_LENGTH);\n const authTag = buf.subarray(IV_LENGTH, IV_LENGTH + AUTH_TAG_LENGTH);\n const ciphertext = buf.subarray(IV_LENGTH + AUTH_TAG_LENGTH);\n\n const decipher = createDecipheriv(ALGORITHM, key, iv, { authTagLength: AUTH_TAG_LENGTH });\n decipher.setAuthTag(authTag);\n\n const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);\n return JSON.parse(decrypted.toString(\"utf8\")) as SessionData;\n } catch {\n return null;\n }\n}\n\nexport interface CookieOptions {\n name: string;\n value: string;\n httpOnly?: boolean;\n secure?: boolean;\n sameSite?: \"lax\" | \"strict\" | \"none\";\n path?: string;\n maxAge?: number;\n}\n\nexport function buildSessionCookie(\n cookieName: string,\n data: SessionData,\n secret: string,\n): CookieOptions {\n return {\n name: cookieName,\n value: encryptSession(data, secret),\n httpOnly: true,\n secure: process.env.NODE_ENV === \"production\",\n sameSite: \"lax\",\n path: \"/\",\n maxAge: 30 * 24 * 60 * 60, // 30 days\n };\n}\n\nexport function buildClearCookie(cookieName: string): CookieOptions {\n return {\n name: cookieName,\n value: \"\",\n httpOnly: true,\n secure: process.env.NODE_ENV === \"production\",\n sameSite: \"lax\",\n path: \"/\",\n maxAge: 0,\n };\n}\n\nexport function buildPKCECookie(\n data: { codeVerifier: string; state: string; returnTo: string },\n secret: string,\n): CookieOptions {\n const key = deriveKey(secret);\n const iv = randomBytes(IV_LENGTH);\n const cipher = createCipheriv(ALGORITHM, key, iv, { authTagLength: AUTH_TAG_LENGTH });\n\n const json = JSON.stringify(data);\n const encrypted = Buffer.concat([cipher.update(json, \"utf8\"), cipher.final()]);\n const authTag = cipher.getAuthTag();\n\n return {\n name: \"authgear.pkce\",\n value: Buffer.concat([iv, authTag, encrypted]).toString(\"base64url\"),\n httpOnly: true,\n secure: process.env.NODE_ENV === \"production\",\n sameSite: \"lax\",\n path: \"/\",\n maxAge: 600, // 10 minutes\n };\n}\n\nexport function decryptPKCECookie(\n encrypted: string,\n secret: string,\n): { codeVerifier: string; state: string; returnTo: string } | null {\n try {\n const key = deriveKey(secret);\n const buf = Buffer.from(encrypted, \"base64url\");\n\n if (buf.length < IV_LENGTH + AUTH_TAG_LENGTH) return null;\n\n const iv = buf.subarray(0, IV_LENGTH);\n const authTag = buf.subarray(IV_LENGTH, IV_LENGTH + AUTH_TAG_LENGTH);\n const ciphertext = buf.subarray(IV_LENGTH + AUTH_TAG_LENGTH);\n\n const decipher = createDecipheriv(ALGORITHM, key, iv, { authTagLength: AUTH_TAG_LENGTH });\n decipher.setAuthTag(authTag);\n\n const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);\n return JSON.parse(decrypted.toString(\"utf8\"));\n } catch {\n return null;\n }\n}\n","import type { OIDCConfiguration, TokenResponse, AppSessionTokenResponse } from \"../types.js\";\n\nexport interface ExchangeCodeParams {\n code: string;\n codeVerifier: string;\n clientID: string;\n redirectURI: string;\n}\n\nexport interface RefreshTokenParams {\n refreshToken: string;\n clientID: string;\n}\n\nexport async function exchangeCode(\n oidcConfig: OIDCConfiguration,\n params: ExchangeCodeParams,\n): Promise<TokenResponse> {\n const body = new URLSearchParams({\n grant_type: \"authorization_code\",\n code: params.code,\n code_verifier: params.codeVerifier,\n client_id: params.clientID,\n redirect_uri: params.redirectURI,\n });\n\n const res = await fetch(oidcConfig.token_endpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: body.toString(),\n });\n\n if (!res.ok) {\n const error = await res.text();\n throw new Error(`Token exchange failed (${res.status}): ${error}`);\n }\n\n return res.json() as Promise<TokenResponse>;\n}\n\nexport async function refreshAccessToken(\n oidcConfig: OIDCConfiguration,\n params: RefreshTokenParams,\n): Promise<TokenResponse> {\n const body = new URLSearchParams({\n grant_type: \"refresh_token\",\n refresh_token: params.refreshToken,\n client_id: params.clientID,\n });\n\n const res = await fetch(oidcConfig.token_endpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: body.toString(),\n });\n\n if (!res.ok) {\n const error = await res.text();\n throw new Error(`Token refresh failed (${res.status}): ${error}`);\n }\n\n return res.json() as Promise<TokenResponse>;\n}\n\nexport async function getAppSessionToken(\n endpoint: string,\n refreshToken: string,\n): Promise<AppSessionTokenResponse> {\n const res = await fetch(`${endpoint}/oauth2/app_session_token`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refresh_token: refreshToken }),\n });\n\n if (!res.ok) {\n const error = await res.text();\n throw new Error(`Failed to get app session token (${res.status}): ${error}`);\n }\n\n const json = (await res.json()) as { result: AppSessionTokenResponse };\n return json.result;\n}\n\nexport async function revokeToken(\n oidcConfig: OIDCConfiguration,\n token: string,\n): Promise<void> {\n await fetch(oidcConfig.revocation_endpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({ token }).toString(),\n });\n}\n","import { SessionState, type SessionData, type Session } from \"../types.js\";\n\nexport function deriveSessionState(data: SessionData | null): Session {\n if (!data) {\n return {\n state: SessionState.NoSession,\n accessToken: null,\n refreshToken: null,\n idToken: null,\n expiresAt: null,\n user: null,\n };\n }\n\n return {\n state: SessionState.Authenticated,\n accessToken: data.accessToken,\n refreshToken: data.refreshToken,\n idToken: data.idToken,\n expiresAt: data.expiresAt,\n user: null, // User is fetched separately when needed\n };\n}\n\nexport function isTokenExpired(expiresAt: number): boolean {\n // Consider expired 30 seconds early for safety margin\n return Date.now() / 1000 >= expiresAt - 30;\n}\n"],"mappings":"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","names":[]}
|
package/dist/client.d.ts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { ReactNode, ButtonHTMLAttributes } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
export { S as Session } from './types-
|
|
3
|
+
import { c as SessionState, U as UserInfo } from './types-D6m4Hact.js';
|
|
4
|
+
export { P as Page, a as PromptOption, S as Session } from './types-D6m4Hact.js';
|
|
5
5
|
|
|
6
6
|
interface SignInOptions {
|
|
7
7
|
returnTo?: string;
|
|
8
8
|
loginPath?: string;
|
|
9
|
+
/**
|
|
10
|
+
* OIDC `prompt` parameter for this sign-in call.
|
|
11
|
+
* Overrides the global `isSSOEnabled` setting for this navigation.
|
|
12
|
+
* Use `PromptOption.Login` or `PromptOption.None` for type-safe values.
|
|
13
|
+
*/
|
|
14
|
+
prompt?: string;
|
|
9
15
|
}
|
|
10
16
|
interface AuthgearProviderProps {
|
|
11
17
|
children: ReactNode;
|
|
@@ -15,8 +21,10 @@ interface AuthgearProviderProps {
|
|
|
15
21
|
loginPath?: string;
|
|
16
22
|
/** Path to the logout route. Defaults to "/api/auth/logout". */
|
|
17
23
|
logoutPath?: string;
|
|
24
|
+
/** Path to the open-page route handler. Defaults to "/api/auth/open". */
|
|
25
|
+
openPagePath?: string;
|
|
18
26
|
}
|
|
19
|
-
declare function AuthgearProvider({ children, userInfoPath, loginPath, logoutPath, }: AuthgearProviderProps): react_jsx_runtime.JSX.Element;
|
|
27
|
+
declare function AuthgearProvider({ children, userInfoPath, loginPath, logoutPath, openPagePath, }: AuthgearProviderProps): react_jsx_runtime.JSX.Element;
|
|
20
28
|
|
|
21
29
|
interface SignInButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
22
30
|
signInOptions?: SignInOptions;
|
|
@@ -26,6 +34,15 @@ declare function SignInButton({ signInOptions, children, ...props }: SignInButto
|
|
|
26
34
|
type SignOutButtonProps = ButtonHTMLAttributes<HTMLButtonElement>;
|
|
27
35
|
declare function SignOutButton({ children, ...props }: SignOutButtonProps): react_jsx_runtime.JSX.Element;
|
|
28
36
|
|
|
37
|
+
type UserSettingsButtonProps = ButtonHTMLAttributes<HTMLButtonElement>;
|
|
38
|
+
/**
|
|
39
|
+
* A button that opens Authgear's account settings page in a new tab
|
|
40
|
+
* for the currently authenticated user. Requires `AuthgearProvider` as an ancestor.
|
|
41
|
+
*
|
|
42
|
+
* Uses `GET /api/auth/open?page=/settings` under the hood — no Server Action needed.
|
|
43
|
+
*/
|
|
44
|
+
declare function UserSettingsButton({ children, ...props }: UserSettingsButtonProps): react_jsx_runtime.JSX.Element;
|
|
45
|
+
|
|
29
46
|
interface UseAuthgearReturn {
|
|
30
47
|
/** Current session state */
|
|
31
48
|
state: SessionState;
|
|
@@ -39,10 +56,12 @@ interface UseAuthgearReturn {
|
|
|
39
56
|
signIn: (options?: SignInOptions) => void;
|
|
40
57
|
/** Navigate to the sign-out endpoint */
|
|
41
58
|
signOut: () => void;
|
|
59
|
+
/** Open an Authgear page (e.g. Page.Settings) in a new tab */
|
|
60
|
+
openPage: (path: string) => void;
|
|
42
61
|
}
|
|
43
62
|
declare function useAuthgear(): UseAuthgearReturn;
|
|
44
63
|
|
|
45
64
|
/** Returns the current user info, or null if not authenticated. */
|
|
46
65
|
declare function useUser(): UserInfo | null;
|
|
47
66
|
|
|
48
|
-
export { AuthgearProvider, type AuthgearProviderProps, SessionState, SignInButton, type SignInButtonProps, type SignInOptions, SignOutButton, type SignOutButtonProps, type UseAuthgearReturn, UserInfo, useAuthgear, useUser };
|
|
67
|
+
export { AuthgearProvider, type AuthgearProviderProps, SessionState, SignInButton, type SignInButtonProps, type SignInOptions, SignOutButton, type SignOutButtonProps, type UseAuthgearReturn, UserInfo, UserSettingsButton, type UserSettingsButtonProps, useAuthgear, useUser };
|
package/dist/client.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import {
|
|
3
|
+
Page,
|
|
4
|
+
PromptOption,
|
|
3
5
|
SessionState
|
|
4
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-AJJAXXPI.js";
|
|
5
7
|
|
|
6
8
|
// src/components/AuthgearProvider.tsx
|
|
7
9
|
import {
|
|
@@ -17,7 +19,8 @@ function AuthgearProvider({
|
|
|
17
19
|
children,
|
|
18
20
|
userInfoPath = "/api/auth/userinfo",
|
|
19
21
|
loginPath = "/api/auth/login",
|
|
20
|
-
logoutPath = "/api/auth/logout"
|
|
22
|
+
logoutPath = "/api/auth/logout",
|
|
23
|
+
openPagePath = "/api/auth/open"
|
|
21
24
|
}) {
|
|
22
25
|
const [state, setState] = useState("UNKNOWN" /* Unknown */);
|
|
23
26
|
const [user, setUser] = useState(null);
|
|
@@ -59,6 +62,9 @@ function AuthgearProvider({
|
|
|
59
62
|
if (options?.returnTo) {
|
|
60
63
|
url.searchParams.set("returnTo", options.returnTo);
|
|
61
64
|
}
|
|
65
|
+
if (options?.prompt != null) {
|
|
66
|
+
url.searchParams.set("prompt", options.prompt);
|
|
67
|
+
}
|
|
62
68
|
window.location.href = url.toString();
|
|
63
69
|
},
|
|
64
70
|
[loginPath]
|
|
@@ -66,7 +72,15 @@ function AuthgearProvider({
|
|
|
66
72
|
const signOut = useCallback(() => {
|
|
67
73
|
window.location.href = logoutPath;
|
|
68
74
|
}, [logoutPath]);
|
|
69
|
-
|
|
75
|
+
const openPage = useCallback(
|
|
76
|
+
(path) => {
|
|
77
|
+
const url = new URL(openPagePath, window.location.origin);
|
|
78
|
+
url.searchParams.set("page", path);
|
|
79
|
+
window.open(url.toString(), "_blank", "noopener,noreferrer");
|
|
80
|
+
},
|
|
81
|
+
[openPagePath]
|
|
82
|
+
);
|
|
83
|
+
return /* @__PURE__ */ jsx(AuthgearContext.Provider, { value: { state, user, isLoaded, signIn, signOut, openPage }, children });
|
|
70
84
|
}
|
|
71
85
|
function useAuthgearContext() {
|
|
72
86
|
const ctx = useContext(AuthgearContext);
|
|
@@ -80,26 +94,46 @@ function useAuthgearContext() {
|
|
|
80
94
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
81
95
|
function SignInButton({ signInOptions, children = "Sign In", ...props }) {
|
|
82
96
|
const { signIn } = useAuthgearContext();
|
|
83
|
-
return /* @__PURE__ */ jsx2("button", { ...props, onClick: () =>
|
|
97
|
+
return /* @__PURE__ */ jsx2("button", { ...props, onClick: (e) => {
|
|
98
|
+
props.onClick?.(e);
|
|
99
|
+
signIn(signInOptions);
|
|
100
|
+
}, children });
|
|
84
101
|
}
|
|
85
102
|
|
|
86
103
|
// src/components/SignOutButton.tsx
|
|
87
104
|
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
88
105
|
function SignOutButton({ children = "Sign Out", ...props }) {
|
|
89
106
|
const { signOut } = useAuthgearContext();
|
|
90
|
-
return /* @__PURE__ */ jsx3("button", { ...props, onClick:
|
|
107
|
+
return /* @__PURE__ */ jsx3("button", { ...props, onClick: (e) => {
|
|
108
|
+
props.onClick?.(e);
|
|
109
|
+
signOut();
|
|
110
|
+
}, children });
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// src/components/UserSettingsButton.tsx
|
|
114
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
115
|
+
function UserSettingsButton({
|
|
116
|
+
children = "Account Settings",
|
|
117
|
+
...props
|
|
118
|
+
}) {
|
|
119
|
+
const { openPage } = useAuthgearContext();
|
|
120
|
+
return /* @__PURE__ */ jsx4("button", { ...props, onClick: (e) => {
|
|
121
|
+
props.onClick?.(e);
|
|
122
|
+
openPage("/settings" /* Settings */);
|
|
123
|
+
}, children });
|
|
91
124
|
}
|
|
92
125
|
|
|
93
126
|
// src/hooks/useAuthgear.ts
|
|
94
127
|
function useAuthgear() {
|
|
95
|
-
const { state, user, isLoaded, signIn, signOut } = useAuthgearContext();
|
|
128
|
+
const { state, user, isLoaded, signIn, signOut, openPage } = useAuthgearContext();
|
|
96
129
|
return {
|
|
97
130
|
state,
|
|
98
131
|
user,
|
|
99
132
|
isLoaded,
|
|
100
133
|
isAuthenticated: state === "AUTHENTICATED" /* Authenticated */,
|
|
101
134
|
signIn,
|
|
102
|
-
signOut
|
|
135
|
+
signOut,
|
|
136
|
+
openPage
|
|
103
137
|
};
|
|
104
138
|
}
|
|
105
139
|
|
|
@@ -110,9 +144,12 @@ function useUser() {
|
|
|
110
144
|
}
|
|
111
145
|
export {
|
|
112
146
|
AuthgearProvider,
|
|
147
|
+
Page,
|
|
148
|
+
PromptOption,
|
|
113
149
|
SessionState,
|
|
114
150
|
SignInButton,
|
|
115
151
|
SignOutButton,
|
|
152
|
+
UserSettingsButton,
|
|
116
153
|
useAuthgear,
|
|
117
154
|
useUser
|
|
118
155
|
};
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/AuthgearProvider.tsx","../src/components/SignInButton.tsx","../src/components/SignOutButton.tsx","../src/hooks/useAuthgear.ts","../src/hooks/useUser.ts"],"sourcesContent":["\"use client\";\n\nimport React, {\n createContext,\n useContext,\n useEffect,\n useState,\n useCallback,\n type ReactNode,\n} from \"react\";\nimport { SessionState, type UserInfo } from \"../types.js\";\n\nexport interface AuthgearContextValue {\n state: SessionState;\n user: UserInfo | null;\n isLoaded: boolean;\n signIn: (options?: SignInOptions) => void;\n signOut: () => void;\n}\n\nexport interface SignInOptions {\n returnTo?: string;\n loginPath?: string;\n}\n\nconst AuthgearContext = createContext<AuthgearContextValue | null>(null);\n\nexport interface AuthgearProviderProps {\n children: ReactNode;\n /** Path to the userinfo API route. Defaults to \"/api/auth/userinfo\". */\n userInfoPath?: string;\n /** Path to the login route. Defaults to \"/api/auth/login\". */\n loginPath?: string;\n /** Path to the logout route. Defaults to \"/api/auth/logout\". */\n logoutPath?: string;\n}\n\nexport function AuthgearProvider({\n children,\n userInfoPath = \"/api/auth/userinfo\",\n loginPath = \"/api/auth/login\",\n logoutPath = \"/api/auth/logout\",\n}: AuthgearProviderProps) {\n const [state, setState] = useState<SessionState>(SessionState.Unknown);\n const [user, setUser] = useState<UserInfo | null>(null);\n const [isLoaded, setIsLoaded] = useState(false);\n\n useEffect(() => {\n let cancelled = false;\n\n async function fetchSession() {\n try {\n const res = await fetch(userInfoPath);\n if (cancelled) return;\n\n if (res.ok) {\n const userInfo = (await res.json()) as UserInfo;\n setState(SessionState.Authenticated);\n setUser(userInfo);\n } else {\n setState(SessionState.NoSession);\n setUser(null);\n }\n } catch {\n if (!cancelled) {\n setState(SessionState.NoSession);\n setUser(null);\n }\n } finally {\n if (!cancelled) {\n setIsLoaded(true);\n }\n }\n }\n\n fetchSession();\n return () => { cancelled = true; };\n }, [userInfoPath]);\n\n const signIn = useCallback(\n (options?: SignInOptions) => {\n const path = options?.loginPath ?? loginPath;\n const url = new URL(path, window.location.origin);\n if (options?.returnTo) {\n url.searchParams.set(\"returnTo\", options.returnTo);\n }\n window.location.href = url.toString();\n },\n [loginPath],\n );\n\n const signOut = useCallback(() => {\n window.location.href = logoutPath;\n }, [logoutPath]);\n\n return (\n <AuthgearContext.Provider value={{ state, user, isLoaded, signIn, signOut }}>\n {children}\n </AuthgearContext.Provider>\n );\n}\n\nexport function useAuthgearContext(): AuthgearContextValue {\n const ctx = useContext(AuthgearContext);\n if (!ctx) {\n throw new Error(\"useAuthgearContext must be used within <AuthgearProvider>\");\n }\n return ctx;\n}\n","\"use client\";\n\nimport React, { type ButtonHTMLAttributes } from \"react\";\nimport { useAuthgearContext, type SignInOptions } from \"./AuthgearProvider.js\";\n\nexport interface SignInButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n signInOptions?: SignInOptions;\n}\n\nexport function SignInButton({ signInOptions, children = \"Sign In\", ...props }: SignInButtonProps) {\n const { signIn } = useAuthgearContext();\n return (\n <button {...props} onClick={() => signIn(signInOptions)}>\n {children}\n </button>\n );\n}\n","\"use client\";\n\nimport React, { type ButtonHTMLAttributes } from \"react\";\nimport { useAuthgearContext } from \"./AuthgearProvider.js\";\n\nexport type SignOutButtonProps = ButtonHTMLAttributes<HTMLButtonElement>;\n\nexport function SignOutButton({ children = \"Sign Out\", ...props }: SignOutButtonProps) {\n const { signOut } = useAuthgearContext();\n return (\n <button {...props} onClick={signOut}>\n {children}\n </button>\n );\n}\n","\"use client\";\n\nimport { useAuthgearContext, type SignInOptions } from \"../components/AuthgearProvider.js\";\nimport { SessionState, type UserInfo } from \"../types.js\";\n\nexport interface UseAuthgearReturn {\n /** Current session state */\n state: SessionState;\n /** Current user info, null if not authenticated */\n user: UserInfo | null;\n /** Whether the initial session check has completed */\n isLoaded: boolean;\n /** Whether the user is currently authenticated */\n isAuthenticated: boolean;\n /** Navigate to the sign-in page */\n signIn: (options?: SignInOptions) => void;\n /** Navigate to the sign-out endpoint */\n signOut: () => void;\n}\n\nexport function useAuthgear(): UseAuthgearReturn {\n const { state, user, isLoaded, signIn, signOut } = useAuthgearContext();\n\n return {\n state,\n user,\n isLoaded,\n isAuthenticated: state === SessionState.Authenticated,\n signIn,\n signOut,\n };\n}\n","\"use client\";\n\nimport { useAuthgearContext } from \"../components/AuthgearProvider.js\";\nimport type { UserInfo } from \"../types.js\";\n\n/** Returns the current user info, or null if not authenticated. */\nexport function useUser(): UserInfo | null {\n const { user } = useAuthgearContext();\n return user;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/components/AuthgearProvider.tsx","../src/components/SignInButton.tsx","../src/components/SignOutButton.tsx","../src/components/UserSettingsButton.tsx","../src/hooks/useAuthgear.ts","../src/hooks/useUser.ts"],"sourcesContent":["\"use client\";\n\nimport React, {\n createContext,\n useContext,\n useEffect,\n useState,\n useCallback,\n type ReactNode,\n} from \"react\";\nimport { SessionState, type UserInfo } from \"../types.js\";\n\nexport interface AuthgearContextValue {\n state: SessionState;\n user: UserInfo | null;\n isLoaded: boolean;\n signIn: (options?: SignInOptions) => void;\n signOut: () => void;\n /** Open an Authgear page (e.g. Page.Settings) in a new tab for the current user */\n openPage: (path: string) => void;\n}\n\nexport interface SignInOptions {\n returnTo?: string;\n loginPath?: string;\n /**\n * OIDC `prompt` parameter for this sign-in call.\n * Overrides the global `isSSOEnabled` setting for this navigation.\n * Use `PromptOption.Login` or `PromptOption.None` for type-safe values.\n */\n prompt?: string;\n}\n\nconst AuthgearContext = createContext<AuthgearContextValue | null>(null);\n\nexport interface AuthgearProviderProps {\n children: ReactNode;\n /** Path to the userinfo API route. Defaults to \"/api/auth/userinfo\". */\n userInfoPath?: string;\n /** Path to the login route. Defaults to \"/api/auth/login\". */\n loginPath?: string;\n /** Path to the logout route. Defaults to \"/api/auth/logout\". */\n logoutPath?: string;\n /** Path to the open-page route handler. Defaults to \"/api/auth/open\". */\n openPagePath?: string;\n}\n\nexport function AuthgearProvider({\n children,\n userInfoPath = \"/api/auth/userinfo\",\n loginPath = \"/api/auth/login\",\n logoutPath = \"/api/auth/logout\",\n openPagePath = \"/api/auth/open\",\n}: AuthgearProviderProps) {\n const [state, setState] = useState<SessionState>(SessionState.Unknown);\n const [user, setUser] = useState<UserInfo | null>(null);\n const [isLoaded, setIsLoaded] = useState(false);\n\n useEffect(() => {\n let cancelled = false;\n\n async function fetchSession() {\n try {\n const res = await fetch(userInfoPath);\n if (cancelled) return;\n\n if (res.ok) {\n const userInfo = (await res.json()) as UserInfo;\n setState(SessionState.Authenticated);\n setUser(userInfo);\n } else {\n setState(SessionState.NoSession);\n setUser(null);\n }\n } catch {\n if (!cancelled) {\n setState(SessionState.NoSession);\n setUser(null);\n }\n } finally {\n if (!cancelled) {\n setIsLoaded(true);\n }\n }\n }\n\n fetchSession();\n return () => { cancelled = true; };\n }, [userInfoPath]);\n\n const signIn = useCallback(\n (options?: SignInOptions) => {\n const path = options?.loginPath ?? loginPath;\n const url = new URL(path, window.location.origin);\n if (options?.returnTo) {\n url.searchParams.set(\"returnTo\", options.returnTo);\n }\n if (options?.prompt != null) {\n url.searchParams.set(\"prompt\", options.prompt);\n }\n window.location.href = url.toString();\n },\n [loginPath],\n );\n\n const signOut = useCallback(() => {\n window.location.href = logoutPath;\n }, [logoutPath]);\n\n const openPage = useCallback(\n (path: string) => {\n const url = new URL(openPagePath, window.location.origin);\n url.searchParams.set(\"page\", path);\n window.open(url.toString(), \"_blank\", \"noopener,noreferrer\");\n },\n [openPagePath],\n );\n\n return (\n <AuthgearContext.Provider value={{ state, user, isLoaded, signIn, signOut, openPage }}>\n {children}\n </AuthgearContext.Provider>\n );\n}\n\nexport function useAuthgearContext(): AuthgearContextValue {\n const ctx = useContext(AuthgearContext);\n if (!ctx) {\n throw new Error(\"useAuthgearContext must be used within <AuthgearProvider>\");\n }\n return ctx;\n}\n","\"use client\";\n\nimport React, { type ButtonHTMLAttributes } from \"react\";\nimport { useAuthgearContext, type SignInOptions } from \"./AuthgearProvider.js\";\n\nexport interface SignInButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n signInOptions?: SignInOptions;\n}\n\nexport function SignInButton({ signInOptions, children = \"Sign In\", ...props }: SignInButtonProps) {\n const { signIn } = useAuthgearContext();\n return (\n <button {...props} onClick={(e) => { props.onClick?.(e); signIn(signInOptions); }}>\n {children}\n </button>\n );\n}\n","\"use client\";\n\nimport React, { type ButtonHTMLAttributes } from \"react\";\nimport { useAuthgearContext } from \"./AuthgearProvider.js\";\n\nexport type SignOutButtonProps = ButtonHTMLAttributes<HTMLButtonElement>;\n\nexport function SignOutButton({ children = \"Sign Out\", ...props }: SignOutButtonProps) {\n const { signOut } = useAuthgearContext();\n return (\n <button {...props} onClick={(e) => { props.onClick?.(e); signOut(); }}>\n {children}\n </button>\n );\n}\n","\"use client\";\n\nimport React, { type ButtonHTMLAttributes } from \"react\";\nimport { useAuthgearContext } from \"./AuthgearProvider.js\";\nimport { Page } from \"../types.js\";\n\nexport type UserSettingsButtonProps = ButtonHTMLAttributes<HTMLButtonElement>;\n\n/**\n * A button that opens Authgear's account settings page in a new tab\n * for the currently authenticated user. Requires `AuthgearProvider` as an ancestor.\n *\n * Uses `GET /api/auth/open?page=/settings` under the hood — no Server Action needed.\n */\nexport function UserSettingsButton({\n children = \"Account Settings\",\n ...props\n}: UserSettingsButtonProps) {\n const { openPage } = useAuthgearContext();\n return (\n <button {...props} onClick={(e) => { props.onClick?.(e); openPage(Page.Settings); }}>\n {children}\n </button>\n );\n}\n","\"use client\";\n\nimport { useAuthgearContext, type SignInOptions } from \"../components/AuthgearProvider.js\";\nimport { SessionState, type UserInfo } from \"../types.js\";\n\nexport interface UseAuthgearReturn {\n /** Current session state */\n state: SessionState;\n /** Current user info, null if not authenticated */\n user: UserInfo | null;\n /** Whether the initial session check has completed */\n isLoaded: boolean;\n /** Whether the user is currently authenticated */\n isAuthenticated: boolean;\n /** Navigate to the sign-in page */\n signIn: (options?: SignInOptions) => void;\n /** Navigate to the sign-out endpoint */\n signOut: () => void;\n /** Open an Authgear page (e.g. Page.Settings) in a new tab */\n openPage: (path: string) => void;\n}\n\nexport function useAuthgear(): UseAuthgearReturn {\n const { state, user, isLoaded, signIn, signOut, openPage } = useAuthgearContext();\n\n return {\n state,\n user,\n isLoaded,\n isAuthenticated: state === SessionState.Authenticated,\n signIn,\n signOut,\n openPage,\n };\n}\n","\"use client\";\n\nimport { useAuthgearContext } from \"../components/AuthgearProvider.js\";\nimport type { UserInfo } from \"../types.js\";\n\n/** Returns the current user info, or null if not authenticated. */\nexport function useUser(): UserInfo | null {\n const { user } = useAuthgearContext();\n return user;\n}\n"],"mappings":"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","names":["jsx","jsx","jsx"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
-
import { A as AuthgearConfig } from './types-
|
|
3
|
-
export { D as DEFAULT_SCOPES, J as JWTPayload, O as OIDCConfiguration, P as Page, S as Session,
|
|
2
|
+
import { A as AuthgearConfig } from './types-D6m4Hact.js';
|
|
3
|
+
export { D as DEFAULT_SCOPES, J as JWTPayload, O as OIDCConfiguration, P as Page, a as PromptOption, S as Session, b as SessionData, c as SessionState, T as TokenResponse, U as UserInfo } from './types-D6m4Hact.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Creates Next.js route handlers for all Authgear auth endpoints.
|
|
@@ -17,6 +17,7 @@ export { D as DEFAULT_SCOPES, J as JWTPayload, O as OIDCConfiguration, P as Page
|
|
|
17
17
|
* - GET /api/auth/logout — Logout and revoke tokens
|
|
18
18
|
* - POST /api/auth/refresh — Refresh access token
|
|
19
19
|
* - GET /api/auth/userinfo — Get current user info
|
|
20
|
+
* - GET /api/auth/open — Open an Authgear page (e.g. /settings) for the current user
|
|
20
21
|
*/
|
|
21
22
|
declare function createAuthgearHandlers(config: AuthgearConfig): {
|
|
22
23
|
GET: (request: NextRequest, { params }: {
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildAuthorizeURL,
|
|
3
|
+
buildOpenURL,
|
|
3
4
|
generateCodeVerifier,
|
|
4
5
|
generateState,
|
|
5
6
|
parseUserInfo
|
|
6
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-GXYXHQVJ.js";
|
|
7
8
|
import {
|
|
8
9
|
buildClearCookie,
|
|
9
10
|
buildPKCECookie,
|
|
@@ -12,19 +13,21 @@ import {
|
|
|
12
13
|
decryptSession,
|
|
13
14
|
exchangeCode,
|
|
14
15
|
fetchOIDCConfiguration,
|
|
16
|
+
getAppSessionToken,
|
|
15
17
|
isTokenExpired,
|
|
16
18
|
refreshAccessToken,
|
|
17
19
|
resolveConfig,
|
|
18
20
|
revokeToken
|
|
19
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-LPGVCNZ6.js";
|
|
20
22
|
import {
|
|
21
23
|
DEFAULT_SCOPES,
|
|
22
24
|
Page,
|
|
25
|
+
PromptOption,
|
|
23
26
|
SessionState
|
|
24
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-AJJAXXPI.js";
|
|
25
28
|
|
|
26
29
|
// src/handlers/index.ts
|
|
27
|
-
import { NextResponse as
|
|
30
|
+
import { NextResponse as NextResponse7 } from "next/server";
|
|
28
31
|
|
|
29
32
|
// src/handlers/login.ts
|
|
30
33
|
import { NextResponse } from "next/server";
|
|
@@ -34,12 +37,17 @@ async function handleLogin(request, config) {
|
|
|
34
37
|
const returnTo = request.nextUrl.searchParams.get("returnTo") ?? "/";
|
|
35
38
|
const codeVerifier = generateCodeVerifier();
|
|
36
39
|
const state = generateState();
|
|
40
|
+
const ALLOWED_PROMPTS = new Set(Object.values(PromptOption));
|
|
41
|
+
const rawPrompt = request.nextUrl.searchParams.get("prompt");
|
|
42
|
+
const perCallPrompt = rawPrompt !== null && ALLOWED_PROMPTS.has(rawPrompt) ? rawPrompt : void 0;
|
|
43
|
+
const prompt = perCallPrompt ?? (resolved.isSSOEnabled ? void 0 : "login");
|
|
37
44
|
const authorizeURL = buildAuthorizeURL(oidcConfig, {
|
|
38
45
|
clientID: resolved.clientID,
|
|
39
46
|
redirectURI: resolved.redirectURI,
|
|
40
47
|
scopes: resolved.scopes,
|
|
41
48
|
codeVerifier,
|
|
42
|
-
state
|
|
49
|
+
state,
|
|
50
|
+
prompt
|
|
43
51
|
});
|
|
44
52
|
const pkceCookie = buildPKCECookie({ codeVerifier, state, returnTo }, resolved.sessionSecret);
|
|
45
53
|
const response = NextResponse.redirect(authorizeURL);
|
|
@@ -226,6 +234,46 @@ async function handleUserInfo(request, config) {
|
|
|
226
234
|
return response;
|
|
227
235
|
}
|
|
228
236
|
|
|
237
|
+
// src/handlers/open.ts
|
|
238
|
+
import { NextResponse as NextResponse6 } from "next/server";
|
|
239
|
+
var ALLOWED_PAGES = new Set(Object.values(Page));
|
|
240
|
+
async function handleOpen(request, config) {
|
|
241
|
+
const resolved = resolveConfig(config);
|
|
242
|
+
const pageParam = request.nextUrl.searchParams.get("page");
|
|
243
|
+
if (!pageParam || !ALLOWED_PAGES.has(pageParam)) {
|
|
244
|
+
return NextResponse6.json({ error: "invalid_page" }, { status: 400 });
|
|
245
|
+
}
|
|
246
|
+
const sessionCookieValue = request.cookies.get(resolved.cookieName)?.value;
|
|
247
|
+
if (!sessionCookieValue) {
|
|
248
|
+
return NextResponse6.json({ error: "not_authenticated" }, { status: 401 });
|
|
249
|
+
}
|
|
250
|
+
const sessionData = decryptSession(sessionCookieValue, resolved.sessionSecret);
|
|
251
|
+
if (!sessionData) {
|
|
252
|
+
return NextResponse6.json({ error: "not_authenticated" }, { status: 401 });
|
|
253
|
+
}
|
|
254
|
+
if (!sessionData.refreshToken) {
|
|
255
|
+
return NextResponse6.json({ error: "no_refresh_token" }, { status: 401 });
|
|
256
|
+
}
|
|
257
|
+
const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);
|
|
258
|
+
let app_session_token;
|
|
259
|
+
try {
|
|
260
|
+
const tokenResponse = await getAppSessionToken(
|
|
261
|
+
resolved.endpoint,
|
|
262
|
+
sessionData.refreshToken
|
|
263
|
+
);
|
|
264
|
+
app_session_token = tokenResponse.app_session_token;
|
|
265
|
+
} catch {
|
|
266
|
+
return NextResponse6.json({ error: "not_authenticated" }, { status: 401 });
|
|
267
|
+
}
|
|
268
|
+
const url = buildOpenURL(oidcConfig, {
|
|
269
|
+
clientID: resolved.clientID,
|
|
270
|
+
appSessionToken: app_session_token,
|
|
271
|
+
targetPath: pageParam,
|
|
272
|
+
scopes: resolved.scopes
|
|
273
|
+
});
|
|
274
|
+
return NextResponse6.redirect(url, 302);
|
|
275
|
+
}
|
|
276
|
+
|
|
229
277
|
// src/handlers/index.ts
|
|
230
278
|
function createAuthgearHandlers(config) {
|
|
231
279
|
async function GET(request, { params }) {
|
|
@@ -240,8 +288,10 @@ function createAuthgearHandlers(config) {
|
|
|
240
288
|
return handleLogout(request, config);
|
|
241
289
|
case "userinfo":
|
|
242
290
|
return handleUserInfo(request, config);
|
|
291
|
+
case "open":
|
|
292
|
+
return handleOpen(request, config);
|
|
243
293
|
default:
|
|
244
|
-
return
|
|
294
|
+
return NextResponse7.json({ error: "not_found" }, { status: 404 });
|
|
245
295
|
}
|
|
246
296
|
}
|
|
247
297
|
async function POST(request, { params }) {
|
|
@@ -251,7 +301,7 @@ function createAuthgearHandlers(config) {
|
|
|
251
301
|
case "refresh":
|
|
252
302
|
return handleRefresh(request, config);
|
|
253
303
|
default:
|
|
254
|
-
return
|
|
304
|
+
return NextResponse7.json({ error: "not_found" }, { status: 404 });
|
|
255
305
|
}
|
|
256
306
|
}
|
|
257
307
|
return { GET, POST };
|
|
@@ -259,6 +309,7 @@ function createAuthgearHandlers(config) {
|
|
|
259
309
|
export {
|
|
260
310
|
DEFAULT_SCOPES,
|
|
261
311
|
Page,
|
|
312
|
+
PromptOption,
|
|
262
313
|
SessionState,
|
|
263
314
|
createAuthgearHandlers
|
|
264
315
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/handlers/index.ts","../src/handlers/login.ts","../src/handlers/callback.ts","../src/handlers/logout.ts","../src/handlers/refresh.ts","../src/handlers/userinfo.ts"],"sourcesContent":["import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig } from \"../types.js\";\nimport { handleLogin } from \"./login.js\";\nimport { handleCallback } from \"./callback.js\";\nimport { handleLogout } from \"./logout.js\";\nimport { handleRefresh } from \"./refresh.js\";\nimport { handleUserInfo } from \"./userinfo.js\";\n\n/**\n * Creates Next.js route handlers for all Authgear auth endpoints.\n *\n * Usage in `app/api/auth/[...authgear]/route.ts`:\n * ```ts\n * import { createAuthgearHandlers } from \"@authgear/nextjs\";\n * export const { GET, POST } = createAuthgearHandlers(config);\n * ```\n *\n * Routes handled:\n * - GET /api/auth/login — Start OAuth flow\n * - GET /api/auth/callback — Handle OAuth callback\n * - GET /api/auth/logout — Logout and revoke tokens\n * - POST /api/auth/refresh — Refresh access token\n * - GET /api/auth/userinfo — Get current user info\n */\nexport function createAuthgearHandlers(config: AuthgearConfig) {\n async function GET(\n request: NextRequest,\n { params }: { params: Promise<{ authgear: string[] }> },\n ): Promise<NextResponse> {\n const { authgear } = await params;\n const action = authgear?.[0];\n\n switch (action) {\n case \"login\":\n return handleLogin(request, config);\n case \"callback\":\n return handleCallback(request, config);\n case \"logout\":\n return handleLogout(request, config);\n case \"userinfo\":\n return handleUserInfo(request, config);\n default:\n return NextResponse.json({ error: \"not_found\" }, { status: 404 });\n }\n }\n\n async function POST(\n request: NextRequest,\n { params }: { params: Promise<{ authgear: string[] }> },\n ): Promise<NextResponse> {\n const { authgear } = await params;\n const action = authgear?.[0];\n\n switch (action) {\n case \"refresh\":\n return handleRefresh(request, config);\n default:\n return NextResponse.json({ error: \"not_found\" }, { status: 404 });\n }\n }\n\n return { GET, POST };\n}\n","import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig } from \"../types.js\";\nimport { resolveConfig } from \"../config.js\";\nimport { fetchOIDCConfiguration } from \"../oauth/discovery.js\";\nimport { generateCodeVerifier } from \"../oauth/pkce.js\";\nimport { buildAuthorizeURL, generateState } from \"../oauth/authorize.js\";\nimport { buildPKCECookie } from \"../session/cookie.js\";\n\nexport async function handleLogin(\n request: NextRequest,\n config: AuthgearConfig,\n): Promise<NextResponse> {\n const resolved = resolveConfig(config);\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n\n const returnTo = request.nextUrl.searchParams.get(\"returnTo\") ?? \"/\";\n const codeVerifier = generateCodeVerifier();\n const state = generateState();\n\n const authorizeURL = buildAuthorizeURL(oidcConfig, {\n clientID: resolved.clientID,\n redirectURI: resolved.redirectURI,\n scopes: resolved.scopes,\n codeVerifier,\n state,\n });\n\n const pkceCookie = buildPKCECookie({ codeVerifier, state, returnTo }, resolved.sessionSecret);\n\n const response = NextResponse.redirect(authorizeURL);\n response.cookies.set(pkceCookie.name, pkceCookie.value, {\n httpOnly: pkceCookie.httpOnly,\n secure: pkceCookie.secure,\n sameSite: pkceCookie.sameSite,\n path: pkceCookie.path,\n maxAge: pkceCookie.maxAge,\n });\n\n return response;\n}\n","import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig } from \"../types.js\";\nimport { resolveConfig } from \"../config.js\";\nimport { fetchOIDCConfiguration } from \"../oauth/discovery.js\";\nimport { exchangeCode } from \"../oauth/token.js\";\nimport { decryptPKCECookie, buildSessionCookie } from \"../session/cookie.js\";\n\nexport async function handleCallback(\n request: NextRequest,\n config: AuthgearConfig,\n): Promise<NextResponse> {\n const resolved = resolveConfig(config);\n\n const code = request.nextUrl.searchParams.get(\"code\");\n const state = request.nextUrl.searchParams.get(\"state\");\n const error = request.nextUrl.searchParams.get(\"error\");\n const errorDescription = request.nextUrl.searchParams.get(\"error_description\");\n\n if (error) {\n return NextResponse.json(\n { error, error_description: errorDescription },\n { status: 400 },\n );\n }\n\n if (!code || !state) {\n return NextResponse.json(\n { error: \"missing_params\", error_description: \"Missing code or state parameter\" },\n { status: 400 },\n );\n }\n\n // Validate PKCE state\n const pkceCookieValue = request.cookies.get(\"authgear.pkce\")?.value;\n if (!pkceCookieValue) {\n return NextResponse.json(\n { error: \"invalid_state\", error_description: \"Missing PKCE cookie\" },\n { status: 400 },\n );\n }\n\n const pkceData = decryptPKCECookie(pkceCookieValue, resolved.sessionSecret);\n if (!pkceData || pkceData.state !== state) {\n return NextResponse.json(\n { error: \"invalid_state\", error_description: \"State mismatch\" },\n { status: 400 },\n );\n }\n\n // Exchange code for tokens\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n const tokenResponse = await exchangeCode(oidcConfig, {\n code,\n codeVerifier: pkceData.codeVerifier,\n clientID: resolved.clientID,\n redirectURI: resolved.redirectURI,\n });\n\n // Build session cookie\n const sessionCookie = buildSessionCookie(\n resolved.cookieName,\n {\n accessToken: tokenResponse.access_token,\n refreshToken: tokenResponse.refresh_token ?? null,\n idToken: tokenResponse.id_token ?? null,\n expiresAt: Math.floor(Date.now() / 1000) + tokenResponse.expires_in,\n },\n resolved.sessionSecret,\n );\n\n const returnTo = pkceData.returnTo || \"/\";\n const redirectURL = new URL(returnTo, request.nextUrl.origin);\n const response = NextResponse.redirect(redirectURL);\n\n // Set session cookie\n response.cookies.set(sessionCookie.name, sessionCookie.value, {\n httpOnly: sessionCookie.httpOnly,\n secure: sessionCookie.secure,\n sameSite: sessionCookie.sameSite,\n path: sessionCookie.path,\n maxAge: sessionCookie.maxAge,\n });\n\n // Clear PKCE cookie\n response.cookies.set(\"authgear.pkce\", \"\", { maxAge: 0, path: \"/\" });\n\n return response;\n}\n","import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig } from \"../types.js\";\nimport { resolveConfig } from \"../config.js\";\nimport { fetchOIDCConfiguration } from \"../oauth/discovery.js\";\nimport { revokeToken } from \"../oauth/token.js\";\nimport { decryptSession, buildClearCookie } from \"../session/cookie.js\";\n\nexport async function handleLogout(\n request: NextRequest,\n config: AuthgearConfig,\n): Promise<NextResponse> {\n const resolved = resolveConfig(config);\n\n // Revoke refresh token if present\n const sessionCookie = request.cookies.get(resolved.cookieName)?.value;\n if (sessionCookie) {\n const session = decryptSession(sessionCookie, resolved.sessionSecret);\n if (session?.refreshToken) {\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n try {\n await revokeToken(oidcConfig, session.refreshToken);\n } catch {\n // Best-effort revocation\n }\n }\n }\n\n const clearCookie = buildClearCookie(resolved.cookieName);\n const redirectURL = new URL(resolved.postLogoutRedirectURI, request.nextUrl.origin);\n const response = NextResponse.redirect(redirectURL);\n\n response.cookies.set(clearCookie.name, clearCookie.value, {\n httpOnly: clearCookie.httpOnly,\n secure: clearCookie.secure,\n sameSite: clearCookie.sameSite,\n path: clearCookie.path,\n maxAge: clearCookie.maxAge,\n });\n\n return response;\n}\n","import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig } from \"../types.js\";\nimport { resolveConfig } from \"../config.js\";\nimport { fetchOIDCConfiguration } from \"../oauth/discovery.js\";\nimport { refreshAccessToken } from \"../oauth/token.js\";\nimport { decryptSession, buildSessionCookie } from \"../session/cookie.js\";\n\nexport async function handleRefresh(\n request: NextRequest,\n config: AuthgearConfig,\n): Promise<NextResponse> {\n const resolved = resolveConfig(config);\n\n const sessionCookieValue = request.cookies.get(resolved.cookieName)?.value;\n if (!sessionCookieValue) {\n return NextResponse.json({ error: \"no_session\" }, { status: 401 });\n }\n\n const session = decryptSession(sessionCookieValue, resolved.sessionSecret);\n if (!session?.refreshToken) {\n return NextResponse.json({ error: \"no_refresh_token\" }, { status: 401 });\n }\n\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n const tokenResponse = await refreshAccessToken(oidcConfig, {\n refreshToken: session.refreshToken,\n clientID: resolved.clientID,\n });\n\n const newSession = {\n accessToken: tokenResponse.access_token,\n refreshToken: tokenResponse.refresh_token ?? session.refreshToken,\n idToken: tokenResponse.id_token ?? session.idToken,\n expiresAt: Math.floor(Date.now() / 1000) + tokenResponse.expires_in,\n };\n\n const sessionCookie = buildSessionCookie(resolved.cookieName, newSession, resolved.sessionSecret);\n const response = NextResponse.json({ ok: true });\n\n response.cookies.set(sessionCookie.name, sessionCookie.value, {\n httpOnly: sessionCookie.httpOnly,\n secure: sessionCookie.secure,\n sameSite: sessionCookie.sameSite,\n path: sessionCookie.path,\n maxAge: sessionCookie.maxAge,\n });\n\n return response;\n}\n","import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig, UserInfo } from \"../types.js\";\nimport { resolveConfig } from \"../config.js\";\nimport { fetchOIDCConfiguration } from \"../oauth/discovery.js\";\nimport { decryptSession } from \"../session/cookie.js\";\nimport { isTokenExpired } from \"../session/state.js\";\nimport { refreshAccessToken } from \"../oauth/token.js\";\nimport { buildSessionCookie } from \"../session/cookie.js\";\nimport { parseUserInfo } from \"../user.js\";\n\nexport async function handleUserInfo(\n request: NextRequest,\n config: AuthgearConfig,\n): Promise<NextResponse> {\n const resolved = resolveConfig(config);\n\n const sessionCookieValue = request.cookies.get(resolved.cookieName)?.value;\n if (!sessionCookieValue) {\n return NextResponse.json({ error: \"no_session\" }, { status: 401 });\n }\n\n let session = decryptSession(sessionCookieValue, resolved.sessionSecret);\n if (!session) {\n return NextResponse.json({ error: \"invalid_session\" }, { status: 401 });\n }\n\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n\n // Auto-refresh if access token is expired\n if (isTokenExpired(session.expiresAt) && session.refreshToken) {\n const tokenResponse = await refreshAccessToken(oidcConfig, {\n refreshToken: session.refreshToken,\n clientID: resolved.clientID,\n });\n session = {\n accessToken: tokenResponse.access_token,\n refreshToken: tokenResponse.refresh_token ?? session.refreshToken,\n idToken: tokenResponse.id_token ?? session.idToken,\n expiresAt: Math.floor(Date.now() / 1000) + tokenResponse.expires_in,\n };\n }\n\n // Fetch user info from Authgear\n const userinfoRes = await fetch(oidcConfig.userinfo_endpoint, {\n headers: { Authorization: `Bearer ${session.accessToken}` },\n });\n\n if (!userinfoRes.ok) {\n return NextResponse.json({ error: \"userinfo_failed\" }, { status: userinfoRes.status });\n }\n\n const raw = (await userinfoRes.json()) as Record<string, unknown>;\n const userInfo: UserInfo = parseUserInfo(raw);\n\n const response = NextResponse.json(userInfo);\n\n // Update session cookie if tokens were refreshed\n const newCookie = buildSessionCookie(resolved.cookieName, session, resolved.sessionSecret);\n response.cookies.set(newCookie.name, newCookie.value, {\n httpOnly: newCookie.httpOnly,\n secure: newCookie.secure,\n sameSite: newCookie.sameSite,\n path: newCookie.path,\n maxAge: newCookie.maxAge,\n });\n\n return response;\n}\n"],"mappings":"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","names":["NextResponse","NextResponse","NextResponse","NextResponse","NextResponse","NextResponse","NextResponse","NextResponse","NextResponse","NextResponse"]}
|
|
1
|
+
{"version":3,"sources":["../src/handlers/index.ts","../src/handlers/login.ts","../src/handlers/callback.ts","../src/handlers/logout.ts","../src/handlers/refresh.ts","../src/handlers/userinfo.ts","../src/handlers/open.ts"],"sourcesContent":["import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig } from \"../types.js\";\nimport { handleLogin } from \"./login.js\";\nimport { handleCallback } from \"./callback.js\";\nimport { handleLogout } from \"./logout.js\";\nimport { handleRefresh } from \"./refresh.js\";\nimport { handleUserInfo } from \"./userinfo.js\";\nimport { handleOpen } from \"./open.js\";\n\n/**\n * Creates Next.js route handlers for all Authgear auth endpoints.\n *\n * Usage in `app/api/auth/[...authgear]/route.ts`:\n * ```ts\n * import { createAuthgearHandlers } from \"@authgear/nextjs\";\n * export const { GET, POST } = createAuthgearHandlers(config);\n * ```\n *\n * Routes handled:\n * - GET /api/auth/login — Start OAuth flow\n * - GET /api/auth/callback — Handle OAuth callback\n * - GET /api/auth/logout — Logout and revoke tokens\n * - POST /api/auth/refresh — Refresh access token\n * - GET /api/auth/userinfo — Get current user info\n * - GET /api/auth/open — Open an Authgear page (e.g. /settings) for the current user\n */\nexport function createAuthgearHandlers(config: AuthgearConfig) {\n async function GET(\n request: NextRequest,\n { params }: { params: Promise<{ authgear: string[] }> },\n ): Promise<NextResponse> {\n const { authgear } = await params;\n const action = authgear?.[0];\n\n switch (action) {\n case \"login\":\n return handleLogin(request, config);\n case \"callback\":\n return handleCallback(request, config);\n case \"logout\":\n return handleLogout(request, config);\n case \"userinfo\":\n return handleUserInfo(request, config);\n case \"open\":\n return handleOpen(request, config);\n default:\n return NextResponse.json({ error: \"not_found\" }, { status: 404 });\n }\n }\n\n async function POST(\n request: NextRequest,\n { params }: { params: Promise<{ authgear: string[] }> },\n ): Promise<NextResponse> {\n const { authgear } = await params;\n const action = authgear?.[0];\n\n switch (action) {\n case \"refresh\":\n return handleRefresh(request, config);\n default:\n return NextResponse.json({ error: \"not_found\" }, { status: 404 });\n }\n }\n\n return { GET, POST };\n}\n","import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig } from \"../types.js\";\nimport { PromptOption } from \"../types.js\";\nimport { resolveConfig } from \"../config.js\";\nimport { fetchOIDCConfiguration } from \"../oauth/discovery.js\";\nimport { generateCodeVerifier } from \"../oauth/pkce.js\";\nimport { buildAuthorizeURL, generateState } from \"../oauth/authorize.js\";\nimport { buildPKCECookie } from \"../session/cookie.js\";\n\nexport async function handleLogin(\n request: NextRequest,\n config: AuthgearConfig,\n): Promise<NextResponse> {\n const resolved = resolveConfig(config);\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n\n const returnTo = request.nextUrl.searchParams.get(\"returnTo\") ?? \"/\";\n const codeVerifier = generateCodeVerifier();\n const state = generateState();\n\n // Per-call ?prompt= query param takes precedence over the global isSSOEnabled default\n const ALLOWED_PROMPTS = new Set<string>(Object.values(PromptOption));\n const rawPrompt = request.nextUrl.searchParams.get(\"prompt\");\n const perCallPrompt = rawPrompt !== null && ALLOWED_PROMPTS.has(rawPrompt) ? rawPrompt : undefined;\n const prompt = perCallPrompt ?? (resolved.isSSOEnabled ? undefined : \"login\");\n\n const authorizeURL = buildAuthorizeURL(oidcConfig, {\n clientID: resolved.clientID,\n redirectURI: resolved.redirectURI,\n scopes: resolved.scopes,\n codeVerifier,\n state,\n prompt,\n });\n\n const pkceCookie = buildPKCECookie({ codeVerifier, state, returnTo }, resolved.sessionSecret);\n\n const response = NextResponse.redirect(authorizeURL);\n response.cookies.set(pkceCookie.name, pkceCookie.value, {\n httpOnly: pkceCookie.httpOnly,\n secure: pkceCookie.secure,\n sameSite: pkceCookie.sameSite,\n path: pkceCookie.path,\n maxAge: pkceCookie.maxAge,\n });\n\n return response;\n}\n","import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig } from \"../types.js\";\nimport { resolveConfig } from \"../config.js\";\nimport { fetchOIDCConfiguration } from \"../oauth/discovery.js\";\nimport { exchangeCode } from \"../oauth/token.js\";\nimport { decryptPKCECookie, buildSessionCookie } from \"../session/cookie.js\";\n\nexport async function handleCallback(\n request: NextRequest,\n config: AuthgearConfig,\n): Promise<NextResponse> {\n const resolved = resolveConfig(config);\n\n const code = request.nextUrl.searchParams.get(\"code\");\n const state = request.nextUrl.searchParams.get(\"state\");\n const error = request.nextUrl.searchParams.get(\"error\");\n const errorDescription = request.nextUrl.searchParams.get(\"error_description\");\n\n if (error) {\n return NextResponse.json(\n { error, error_description: errorDescription },\n { status: 400 },\n );\n }\n\n if (!code || !state) {\n return NextResponse.json(\n { error: \"missing_params\", error_description: \"Missing code or state parameter\" },\n { status: 400 },\n );\n }\n\n // Validate PKCE state\n const pkceCookieValue = request.cookies.get(\"authgear.pkce\")?.value;\n if (!pkceCookieValue) {\n return NextResponse.json(\n { error: \"invalid_state\", error_description: \"Missing PKCE cookie\" },\n { status: 400 },\n );\n }\n\n const pkceData = decryptPKCECookie(pkceCookieValue, resolved.sessionSecret);\n if (!pkceData || pkceData.state !== state) {\n return NextResponse.json(\n { error: \"invalid_state\", error_description: \"State mismatch\" },\n { status: 400 },\n );\n }\n\n // Exchange code for tokens\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n const tokenResponse = await exchangeCode(oidcConfig, {\n code,\n codeVerifier: pkceData.codeVerifier,\n clientID: resolved.clientID,\n redirectURI: resolved.redirectURI,\n });\n\n // Build session cookie\n const sessionCookie = buildSessionCookie(\n resolved.cookieName,\n {\n accessToken: tokenResponse.access_token,\n refreshToken: tokenResponse.refresh_token ?? null,\n idToken: tokenResponse.id_token ?? null,\n expiresAt: Math.floor(Date.now() / 1000) + tokenResponse.expires_in,\n },\n resolved.sessionSecret,\n );\n\n const returnTo = pkceData.returnTo || \"/\";\n const redirectURL = new URL(returnTo, request.nextUrl.origin);\n const response = NextResponse.redirect(redirectURL);\n\n // Set session cookie\n response.cookies.set(sessionCookie.name, sessionCookie.value, {\n httpOnly: sessionCookie.httpOnly,\n secure: sessionCookie.secure,\n sameSite: sessionCookie.sameSite,\n path: sessionCookie.path,\n maxAge: sessionCookie.maxAge,\n });\n\n // Clear PKCE cookie\n response.cookies.set(\"authgear.pkce\", \"\", { maxAge: 0, path: \"/\" });\n\n return response;\n}\n","import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig } from \"../types.js\";\nimport { resolveConfig } from \"../config.js\";\nimport { fetchOIDCConfiguration } from \"../oauth/discovery.js\";\nimport { revokeToken } from \"../oauth/token.js\";\nimport { decryptSession, buildClearCookie } from \"../session/cookie.js\";\n\nexport async function handleLogout(\n request: NextRequest,\n config: AuthgearConfig,\n): Promise<NextResponse> {\n const resolved = resolveConfig(config);\n\n // Revoke refresh token if present\n const sessionCookie = request.cookies.get(resolved.cookieName)?.value;\n if (sessionCookie) {\n const session = decryptSession(sessionCookie, resolved.sessionSecret);\n if (session?.refreshToken) {\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n try {\n await revokeToken(oidcConfig, session.refreshToken);\n } catch {\n // Best-effort revocation\n }\n }\n }\n\n const clearCookie = buildClearCookie(resolved.cookieName);\n const redirectURL = new URL(resolved.postLogoutRedirectURI, request.nextUrl.origin);\n const response = NextResponse.redirect(redirectURL);\n\n response.cookies.set(clearCookie.name, clearCookie.value, {\n httpOnly: clearCookie.httpOnly,\n secure: clearCookie.secure,\n sameSite: clearCookie.sameSite,\n path: clearCookie.path,\n maxAge: clearCookie.maxAge,\n });\n\n return response;\n}\n","import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig } from \"../types.js\";\nimport { resolveConfig } from \"../config.js\";\nimport { fetchOIDCConfiguration } from \"../oauth/discovery.js\";\nimport { refreshAccessToken } from \"../oauth/token.js\";\nimport { decryptSession, buildSessionCookie } from \"../session/cookie.js\";\n\nexport async function handleRefresh(\n request: NextRequest,\n config: AuthgearConfig,\n): Promise<NextResponse> {\n const resolved = resolveConfig(config);\n\n const sessionCookieValue = request.cookies.get(resolved.cookieName)?.value;\n if (!sessionCookieValue) {\n return NextResponse.json({ error: \"no_session\" }, { status: 401 });\n }\n\n const session = decryptSession(sessionCookieValue, resolved.sessionSecret);\n if (!session?.refreshToken) {\n return NextResponse.json({ error: \"no_refresh_token\" }, { status: 401 });\n }\n\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n const tokenResponse = await refreshAccessToken(oidcConfig, {\n refreshToken: session.refreshToken,\n clientID: resolved.clientID,\n });\n\n const newSession = {\n accessToken: tokenResponse.access_token,\n refreshToken: tokenResponse.refresh_token ?? session.refreshToken,\n idToken: tokenResponse.id_token ?? session.idToken,\n expiresAt: Math.floor(Date.now() / 1000) + tokenResponse.expires_in,\n };\n\n const sessionCookie = buildSessionCookie(resolved.cookieName, newSession, resolved.sessionSecret);\n const response = NextResponse.json({ ok: true });\n\n response.cookies.set(sessionCookie.name, sessionCookie.value, {\n httpOnly: sessionCookie.httpOnly,\n secure: sessionCookie.secure,\n sameSite: sessionCookie.sameSite,\n path: sessionCookie.path,\n maxAge: sessionCookie.maxAge,\n });\n\n return response;\n}\n","import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig, UserInfo } from \"../types.js\";\nimport { resolveConfig } from \"../config.js\";\nimport { fetchOIDCConfiguration } from \"../oauth/discovery.js\";\nimport { decryptSession } from \"../session/cookie.js\";\nimport { isTokenExpired } from \"../session/state.js\";\nimport { refreshAccessToken } from \"../oauth/token.js\";\nimport { buildSessionCookie } from \"../session/cookie.js\";\nimport { parseUserInfo } from \"../user.js\";\n\nexport async function handleUserInfo(\n request: NextRequest,\n config: AuthgearConfig,\n): Promise<NextResponse> {\n const resolved = resolveConfig(config);\n\n const sessionCookieValue = request.cookies.get(resolved.cookieName)?.value;\n if (!sessionCookieValue) {\n return NextResponse.json({ error: \"no_session\" }, { status: 401 });\n }\n\n let session = decryptSession(sessionCookieValue, resolved.sessionSecret);\n if (!session) {\n return NextResponse.json({ error: \"invalid_session\" }, { status: 401 });\n }\n\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n\n // Auto-refresh if access token is expired\n if (isTokenExpired(session.expiresAt) && session.refreshToken) {\n const tokenResponse = await refreshAccessToken(oidcConfig, {\n refreshToken: session.refreshToken,\n clientID: resolved.clientID,\n });\n session = {\n accessToken: tokenResponse.access_token,\n refreshToken: tokenResponse.refresh_token ?? session.refreshToken,\n idToken: tokenResponse.id_token ?? session.idToken,\n expiresAt: Math.floor(Date.now() / 1000) + tokenResponse.expires_in,\n };\n }\n\n // Fetch user info from Authgear\n const userinfoRes = await fetch(oidcConfig.userinfo_endpoint, {\n headers: { Authorization: `Bearer ${session.accessToken}` },\n });\n\n if (!userinfoRes.ok) {\n return NextResponse.json({ error: \"userinfo_failed\" }, { status: userinfoRes.status });\n }\n\n const raw = (await userinfoRes.json()) as Record<string, unknown>;\n const userInfo: UserInfo = parseUserInfo(raw);\n\n const response = NextResponse.json(userInfo);\n\n // Update session cookie if tokens were refreshed\n const newCookie = buildSessionCookie(resolved.cookieName, session, resolved.sessionSecret);\n response.cookies.set(newCookie.name, newCookie.value, {\n httpOnly: newCookie.httpOnly,\n secure: newCookie.secure,\n sameSite: newCookie.sameSite,\n path: newCookie.path,\n maxAge: newCookie.maxAge,\n });\n\n return response;\n}\n","import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig } from \"../types.js\";\nimport { Page } from \"../types.js\";\nimport { resolveConfig } from \"../config.js\";\nimport { fetchOIDCConfiguration } from \"../oauth/discovery.js\";\nimport { getAppSessionToken } from \"../oauth/token.js\";\nimport { buildOpenURL } from \"../oauth/authorize.js\";\nimport { decryptSession } from \"../session/cookie.js\";\n\nconst ALLOWED_PAGES = new Set<string>(Object.values(Page));\n\nexport async function handleOpen(\n request: NextRequest,\n config: AuthgearConfig,\n): Promise<NextResponse> {\n const resolved = resolveConfig(config);\n\n const pageParam = request.nextUrl.searchParams.get(\"page\");\n if (!pageParam || !ALLOWED_PAGES.has(pageParam)) {\n return NextResponse.json({ error: \"invalid_page\" }, { status: 400 });\n }\n\n const sessionCookieValue = request.cookies.get(resolved.cookieName)?.value;\n if (!sessionCookieValue) {\n return NextResponse.json({ error: \"not_authenticated\" }, { status: 401 });\n }\n\n const sessionData = decryptSession(sessionCookieValue, resolved.sessionSecret);\n if (!sessionData) {\n return NextResponse.json({ error: \"not_authenticated\" }, { status: 401 });\n }\n\n if (!sessionData.refreshToken) {\n return NextResponse.json({ error: \"no_refresh_token\" }, { status: 401 });\n }\n\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n\n let app_session_token: string;\n try {\n const tokenResponse = await getAppSessionToken(\n resolved.endpoint,\n sessionData.refreshToken,\n );\n app_session_token = tokenResponse.app_session_token;\n } catch {\n return NextResponse.json({ error: \"not_authenticated\" }, { status: 401 });\n }\n\n const url = buildOpenURL(oidcConfig, {\n clientID: resolved.clientID,\n appSessionToken: app_session_token,\n targetPath: pageParam,\n scopes: resolved.scopes,\n });\n\n return NextResponse.redirect(url, 302);\n}\n"],"mappings":"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","names":["NextResponse","NextResponse","NextResponse","NextResponse","NextResponse","NextResponse","NextResponse","NextResponse","NextResponse","NextResponse","NextResponse","NextResponse"]}
|
package/dist/proxy.d.ts
CHANGED
package/dist/proxy.js
CHANGED
|
@@ -5,8 +5,8 @@ import {
|
|
|
5
5
|
isTokenExpired,
|
|
6
6
|
refreshAccessToken,
|
|
7
7
|
resolveConfig
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
8
|
+
} from "./chunk-LPGVCNZ6.js";
|
|
9
|
+
import "./chunk-AJJAXXPI.js";
|
|
10
10
|
|
|
11
11
|
// src/proxy.ts
|
|
12
12
|
import { NextResponse } from "next/server";
|
package/dist/server.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as AuthgearConfig, S as Session, U as UserInfo, P as Page, J as JWTPayload } from './types-
|
|
2
|
-
export {
|
|
1
|
+
import { A as AuthgearConfig, S as Session, U as UserInfo, P as Page, J as JWTPayload } from './types-D6m4Hact.js';
|
|
2
|
+
export { a as PromptOption, c as SessionState } from './types-D6m4Hact.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Read the current session in a Server Component, Route Handler, or Server Action.
|
package/dist/server.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildOpenURL,
|
|
3
3
|
parseUserInfo
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-GXYXHQVJ.js";
|
|
5
5
|
import {
|
|
6
6
|
buildSessionCookie,
|
|
7
7
|
decryptSession,
|
|
@@ -11,11 +11,12 @@ import {
|
|
|
11
11
|
isTokenExpired,
|
|
12
12
|
refreshAccessToken,
|
|
13
13
|
resolveConfig
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-LPGVCNZ6.js";
|
|
15
15
|
import {
|
|
16
16
|
Page,
|
|
17
|
+
PromptOption,
|
|
17
18
|
SessionState
|
|
18
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-AJJAXXPI.js";
|
|
19
20
|
|
|
20
21
|
// src/server.ts
|
|
21
22
|
import "server-only";
|
|
@@ -153,6 +154,7 @@ async function getOpenURL(page, config) {
|
|
|
153
154
|
}
|
|
154
155
|
export {
|
|
155
156
|
Page,
|
|
157
|
+
PromptOption,
|
|
156
158
|
SessionState,
|
|
157
159
|
auth,
|
|
158
160
|
currentUser,
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server.ts","../src/jwt/verify.ts","../src/jwt/jwks.ts"],"sourcesContent":["import \"server-only\";\nimport { cookies } from \"next/headers\";\nimport { SessionState, Page, type Session, type UserInfo, type JWTPayload, type AuthgearConfig } from \"./types.js\";\nimport { resolveConfig } from \"./config.js\";\nimport { decryptSession, buildSessionCookie } from \"./session/cookie.js\";\nimport { deriveSessionState, isTokenExpired } from \"./session/state.js\";\nimport { fetchOIDCConfiguration } from \"./oauth/discovery.js\";\nimport { refreshAccessToken, getAppSessionToken } from \"./oauth/token.js\";\nimport { buildOpenURL } from \"./oauth/authorize.js\";\nimport { verifyJWT } from \"./jwt/verify.js\";\nimport { parseUserInfo } from \"./user.js\";\n\n/**\n * Read the current session in a Server Component, Route Handler, or Server Action.\n * Automatically refreshes the access token if expired, so `session.accessToken` is\n * always valid when the session state is `Authenticated`. Use this when you need a\n * fresh access token to call a downstream API (e.g. inside a Server Action).\n */\nexport async function auth(config: AuthgearConfig): Promise<Session> {\n const resolved = resolveConfig(config);\n const cookieStore = await cookies();\n const sessionCookieValue = cookieStore.get(resolved.cookieName)?.value;\n\n let sessionData = sessionCookieValue\n ? decryptSession(sessionCookieValue, resolved.sessionSecret)\n : null;\n\n // Auto-refresh expired token so callers (e.g. Server Actions) always get a valid access token\n if (sessionData && isTokenExpired(sessionData.expiresAt) && sessionData.refreshToken) {\n try {\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n const tokenResponse = await refreshAccessToken(oidcConfig, {\n refreshToken: sessionData.refreshToken,\n clientID: resolved.clientID,\n });\n sessionData = {\n accessToken: tokenResponse.access_token,\n refreshToken: tokenResponse.refresh_token ?? sessionData.refreshToken,\n idToken: tokenResponse.id_token ?? sessionData.idToken,\n expiresAt: Math.floor(Date.now() / 1000) + tokenResponse.expires_in,\n };\n try {\n const newCookie = buildSessionCookie(resolved.cookieName, sessionData, resolved.sessionSecret);\n cookieStore.set(newCookie.name, newCookie.value, {\n httpOnly: newCookie.httpOnly,\n secure: newCookie.secure,\n sameSite: newCookie.sameSite,\n path: newCookie.path,\n maxAge: newCookie.maxAge,\n });\n } catch {\n // Not in a Route Handler — cookie will be persisted by the proxy on next navigation\n }\n } catch {\n sessionData = null;\n }\n }\n\n return deriveSessionState(sessionData);\n}\n\n/**\n * Get the current user in a Server Component or Route Handler.\n * Automatically refreshes the access token if expired, including persisting a\n * rotated refresh token when the Authgear project has refresh token rotation enabled.\n * Returns null if not authenticated or if the session cannot be refreshed.\n */\nexport async function currentUser(config: AuthgearConfig): Promise<UserInfo | null> {\n const resolved = resolveConfig(config);\n const cookieStore = await cookies();\n const sessionCookieValue = cookieStore.get(resolved.cookieName)?.value;\n\n if (!sessionCookieValue) return null;\n\n let sessionData = decryptSession(sessionCookieValue, resolved.sessionSecret);\n if (!sessionData) return null;\n\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n\n // Auto-refresh expired token\n if (isTokenExpired(sessionData.expiresAt) && sessionData.refreshToken) {\n try {\n const tokenResponse = await refreshAccessToken(oidcConfig, {\n refreshToken: sessionData.refreshToken,\n clientID: resolved.clientID,\n });\n sessionData = {\n accessToken: tokenResponse.access_token,\n refreshToken: tokenResponse.refresh_token ?? sessionData.refreshToken,\n idToken: tokenResponse.id_token ?? sessionData.idToken,\n expiresAt: Math.floor(Date.now() / 1000) + tokenResponse.expires_in,\n };\n // Persist the updated session (with rotated refresh token) back to the cookie.\n // This succeeds in Route Handlers but throws in Server Components (Next.js restriction).\n // In Server Components the proxy will write the updated cookie on the next page navigation.\n try {\n const newCookie = buildSessionCookie(resolved.cookieName, sessionData, resolved.sessionSecret);\n cookieStore.set(newCookie.name, newCookie.value, {\n httpOnly: newCookie.httpOnly,\n secure: newCookie.secure,\n sameSite: newCookie.sameSite,\n path: newCookie.path,\n maxAge: newCookie.maxAge,\n });\n } catch {\n // Not in a Route Handler — cookie will be persisted by the proxy on next navigation\n }\n } catch {\n return null;\n }\n }\n\n const userinfoRes = await fetch(oidcConfig.userinfo_endpoint, {\n headers: { Authorization: `Bearer ${sessionData.accessToken}` },\n });\n\n if (!userinfoRes.ok) return null;\n\n const raw = (await userinfoRes.json()) as Record<string, unknown>;\n return parseUserInfo(raw);\n}\n\n/**\n * Verify a JWT access token (from Authorization: Bearer header).\n * Useful for protecting API routes.\n *\n * @throws {Error} If the token is invalid, expired, or has wrong issuer/audience\n */\nexport async function verifyAccessToken(\n token: string,\n config: AuthgearConfig,\n): Promise<JWTPayload> {\n const resolved = resolveConfig(config);\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n return verifyJWT(token, oidcConfig);\n}\n\n/**\n * Get a URL that opens an Authgear page (e.g. `/settings`) with the current\n * user already authenticated — no re-login required.\n *\n * Exchanges the user's refresh token for a short-lived `app_session_token`\n * via `POST /oauth2/app_session_token`, then builds an authorization URL\n * that uses that token as a `login_hint` so Authgear can authenticate the\n * user silently.\n *\n * @param page - A `Page` enum value (e.g. `Page.Settings`) or an arbitrary path string.\n * @param config - The Authgear SDK config.\n * @returns A URL string. Open it in a new tab (`window.open(url, \"_blank\")`).\n * @throws {Error} If the user is not authenticated or has no refresh token.\n *\n * @example\n * ```ts\n * // Server Action\n * \"use server\";\n * import { getOpenURL, Page } from \"@authgear/nextjs/server\";\n * import { authgearConfig } from \"@/lib/authgear\";\n *\n * export async function getSettingsURLAction() {\n * return getOpenURL(Page.Settings, authgearConfig);\n * }\n * ```\n */\nexport async function getOpenURL(\n page: Page | string,\n config: AuthgearConfig,\n): Promise<string> {\n const resolved = resolveConfig(config);\n const cookieStore = await cookies();\n const sessionCookieValue = cookieStore.get(resolved.cookieName)?.value;\n if (!sessionCookieValue) throw new Error(\"Not authenticated\");\n let sessionData = decryptSession(sessionCookieValue, resolved.sessionSecret);\n if (!sessionData) throw new Error(\"Not authenticated\");\n if (!sessionData.refreshToken) throw new Error(\"No refresh token in session\");\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n const { app_session_token } = await getAppSessionToken(\n resolved.endpoint,\n sessionData.refreshToken!,\n );\n return buildOpenURL(oidcConfig, {\n clientID: resolved.clientID,\n appSessionToken: app_session_token,\n targetPath: page,\n scopes: resolved.scopes,\n });\n}\n\nexport { SessionState, Page };\nexport type { Session, UserInfo, JWTPayload };\n","import { jwtVerify } from \"jose\";\nimport type { JWTPayload, OIDCConfiguration } from \"../types.js\";\nimport { getJWKS } from \"./jwks.js\";\n\nexport interface VerifyOptions {\n /** Expected audience. If not set, audience is not checked. */\n audience?: string | string[];\n}\n\nexport async function verifyJWT(\n token: string,\n oidcConfig: OIDCConfiguration,\n options?: VerifyOptions,\n): Promise<JWTPayload> {\n const jwks = getJWKS(oidcConfig);\n\n const { payload } = await jwtVerify(token, jwks, {\n issuer: oidcConfig.issuer,\n audience: options?.audience,\n algorithms: [\"RS256\"],\n });\n\n return payload as unknown as JWTPayload;\n}\n","import { createRemoteJWKSet } from \"jose\";\nimport type { OIDCConfiguration } from \"../types.js\";\n\nconst jwksSets = new Map<string, ReturnType<typeof createRemoteJWKSet>>();\n\nexport function getJWKS(oidcConfig: OIDCConfiguration) {\n const uri = oidcConfig.jwks_uri;\n let jwks = jwksSets.get(uri);\n if (!jwks) {\n jwks = createRemoteJWKSet(new URL(uri));\n jwksSets.set(uri, jwks);\n }\n return jwks;\n}\n\n/** Clear cached JWKS (useful for testing) */\nexport function clearJWKSCache(): void {\n jwksSets.clear();\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/server.ts","../src/jwt/verify.ts","../src/jwt/jwks.ts"],"sourcesContent":["import \"server-only\";\nimport { cookies } from \"next/headers\";\nimport { SessionState, Page, PromptOption, type Session, type UserInfo, type JWTPayload, type AuthgearConfig } from \"./types.js\";\nimport { resolveConfig } from \"./config.js\";\nimport { decryptSession, buildSessionCookie } from \"./session/cookie.js\";\nimport { deriveSessionState, isTokenExpired } from \"./session/state.js\";\nimport { fetchOIDCConfiguration } from \"./oauth/discovery.js\";\nimport { refreshAccessToken, getAppSessionToken } from \"./oauth/token.js\";\nimport { buildOpenURL } from \"./oauth/authorize.js\";\nimport { verifyJWT } from \"./jwt/verify.js\";\nimport { parseUserInfo } from \"./user.js\";\n\n/**\n * Read the current session in a Server Component, Route Handler, or Server Action.\n * Automatically refreshes the access token if expired, so `session.accessToken` is\n * always valid when the session state is `Authenticated`. Use this when you need a\n * fresh access token to call a downstream API (e.g. inside a Server Action).\n */\nexport async function auth(config: AuthgearConfig): Promise<Session> {\n const resolved = resolveConfig(config);\n const cookieStore = await cookies();\n const sessionCookieValue = cookieStore.get(resolved.cookieName)?.value;\n\n let sessionData = sessionCookieValue\n ? decryptSession(sessionCookieValue, resolved.sessionSecret)\n : null;\n\n // Auto-refresh expired token so callers (e.g. Server Actions) always get a valid access token\n if (sessionData && isTokenExpired(sessionData.expiresAt) && sessionData.refreshToken) {\n try {\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n const tokenResponse = await refreshAccessToken(oidcConfig, {\n refreshToken: sessionData.refreshToken,\n clientID: resolved.clientID,\n });\n sessionData = {\n accessToken: tokenResponse.access_token,\n refreshToken: tokenResponse.refresh_token ?? sessionData.refreshToken,\n idToken: tokenResponse.id_token ?? sessionData.idToken,\n expiresAt: Math.floor(Date.now() / 1000) + tokenResponse.expires_in,\n };\n try {\n const newCookie = buildSessionCookie(resolved.cookieName, sessionData, resolved.sessionSecret);\n cookieStore.set(newCookie.name, newCookie.value, {\n httpOnly: newCookie.httpOnly,\n secure: newCookie.secure,\n sameSite: newCookie.sameSite,\n path: newCookie.path,\n maxAge: newCookie.maxAge,\n });\n } catch {\n // Not in a Route Handler — cookie will be persisted by the proxy on next navigation\n }\n } catch {\n sessionData = null;\n }\n }\n\n return deriveSessionState(sessionData);\n}\n\n/**\n * Get the current user in a Server Component or Route Handler.\n * Automatically refreshes the access token if expired, including persisting a\n * rotated refresh token when the Authgear project has refresh token rotation enabled.\n * Returns null if not authenticated or if the session cannot be refreshed.\n */\nexport async function currentUser(config: AuthgearConfig): Promise<UserInfo | null> {\n const resolved = resolveConfig(config);\n const cookieStore = await cookies();\n const sessionCookieValue = cookieStore.get(resolved.cookieName)?.value;\n\n if (!sessionCookieValue) return null;\n\n let sessionData = decryptSession(sessionCookieValue, resolved.sessionSecret);\n if (!sessionData) return null;\n\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n\n // Auto-refresh expired token\n if (isTokenExpired(sessionData.expiresAt) && sessionData.refreshToken) {\n try {\n const tokenResponse = await refreshAccessToken(oidcConfig, {\n refreshToken: sessionData.refreshToken,\n clientID: resolved.clientID,\n });\n sessionData = {\n accessToken: tokenResponse.access_token,\n refreshToken: tokenResponse.refresh_token ?? sessionData.refreshToken,\n idToken: tokenResponse.id_token ?? sessionData.idToken,\n expiresAt: Math.floor(Date.now() / 1000) + tokenResponse.expires_in,\n };\n // Persist the updated session (with rotated refresh token) back to the cookie.\n // This succeeds in Route Handlers but throws in Server Components (Next.js restriction).\n // In Server Components the proxy will write the updated cookie on the next page navigation.\n try {\n const newCookie = buildSessionCookie(resolved.cookieName, sessionData, resolved.sessionSecret);\n cookieStore.set(newCookie.name, newCookie.value, {\n httpOnly: newCookie.httpOnly,\n secure: newCookie.secure,\n sameSite: newCookie.sameSite,\n path: newCookie.path,\n maxAge: newCookie.maxAge,\n });\n } catch {\n // Not in a Route Handler — cookie will be persisted by the proxy on next navigation\n }\n } catch {\n return null;\n }\n }\n\n const userinfoRes = await fetch(oidcConfig.userinfo_endpoint, {\n headers: { Authorization: `Bearer ${sessionData.accessToken}` },\n });\n\n if (!userinfoRes.ok) return null;\n\n const raw = (await userinfoRes.json()) as Record<string, unknown>;\n return parseUserInfo(raw);\n}\n\n/**\n * Verify a JWT access token (from Authorization: Bearer header).\n * Useful for protecting API routes.\n *\n * @throws {Error} If the token is invalid, expired, or has wrong issuer/audience\n */\nexport async function verifyAccessToken(\n token: string,\n config: AuthgearConfig,\n): Promise<JWTPayload> {\n const resolved = resolveConfig(config);\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n return verifyJWT(token, oidcConfig);\n}\n\n/**\n * Get a URL that opens an Authgear page (e.g. `/settings`) with the current\n * user already authenticated — no re-login required.\n *\n * Exchanges the user's refresh token for a short-lived `app_session_token`\n * via `POST /oauth2/app_session_token`, then builds an authorization URL\n * that uses that token as a `login_hint` so Authgear can authenticate the\n * user silently.\n *\n * @param page - A `Page` enum value (e.g. `Page.Settings`) or an arbitrary path string.\n * @param config - The Authgear SDK config.\n * @returns A URL string. Open it in a new tab (`window.open(url, \"_blank\")`).\n * @throws {Error} If the user is not authenticated or has no refresh token.\n *\n * @example\n * ```ts\n * // Server Action\n * \"use server\";\n * import { getOpenURL, Page } from \"@authgear/nextjs/server\";\n * import { authgearConfig } from \"@/lib/authgear\";\n *\n * export async function getSettingsURLAction() {\n * return getOpenURL(Page.Settings, authgearConfig);\n * }\n * ```\n */\nexport async function getOpenURL(\n page: Page | string,\n config: AuthgearConfig,\n): Promise<string> {\n const resolved = resolveConfig(config);\n const cookieStore = await cookies();\n const sessionCookieValue = cookieStore.get(resolved.cookieName)?.value;\n if (!sessionCookieValue) throw new Error(\"Not authenticated\");\n let sessionData = decryptSession(sessionCookieValue, resolved.sessionSecret);\n if (!sessionData) throw new Error(\"Not authenticated\");\n if (!sessionData.refreshToken) throw new Error(\"No refresh token in session\");\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n const { app_session_token } = await getAppSessionToken(\n resolved.endpoint,\n sessionData.refreshToken!,\n );\n return buildOpenURL(oidcConfig, {\n clientID: resolved.clientID,\n appSessionToken: app_session_token,\n targetPath: page,\n scopes: resolved.scopes,\n });\n}\n\nexport { SessionState, Page, PromptOption };\nexport type { Session, UserInfo, JWTPayload };\n","import { jwtVerify } from \"jose\";\nimport type { JWTPayload, OIDCConfiguration } from \"../types.js\";\nimport { getJWKS } from \"./jwks.js\";\n\nexport interface VerifyOptions {\n /** Expected audience. If not set, audience is not checked. */\n audience?: string | string[];\n}\n\nexport async function verifyJWT(\n token: string,\n oidcConfig: OIDCConfiguration,\n options?: VerifyOptions,\n): Promise<JWTPayload> {\n const jwks = getJWKS(oidcConfig);\n\n const { payload } = await jwtVerify(token, jwks, {\n issuer: oidcConfig.issuer,\n audience: options?.audience,\n algorithms: [\"RS256\"],\n });\n\n return payload as unknown as JWTPayload;\n}\n","import { createRemoteJWKSet } from \"jose\";\nimport type { OIDCConfiguration } from \"../types.js\";\n\nconst jwksSets = new Map<string, ReturnType<typeof createRemoteJWKSet>>();\n\nexport function getJWKS(oidcConfig: OIDCConfiguration) {\n const uri = oidcConfig.jwks_uri;\n let jwks = jwksSets.get(uri);\n if (!jwks) {\n jwks = createRemoteJWKSet(new URL(uri));\n jwksSets.set(uri, jwks);\n }\n return jwks;\n}\n\n/** Clear cached JWKS (useful for testing) */\nexport function clearJWKSCache(): void {\n jwksSets.clear();\n}\n"],"mappings":"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","names":[]}
|
|
@@ -13,13 +13,37 @@ interface AuthgearConfig {
|
|
|
13
13
|
sessionSecret: string;
|
|
14
14
|
/** Session cookie name. Defaults to "authgear.session" */
|
|
15
15
|
cookieName?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Whether to enable SSO (Single Sign-On) with other apps on the same Authgear tenant.
|
|
18
|
+
* When `true` (default), Authgear silently reuses its server-side session if the user
|
|
19
|
+
* is already logged in, so users are not prompted for credentials again.
|
|
20
|
+
* Set to `false` to always show the login form (`prompt=login`), which is recommended
|
|
21
|
+
* for single-app deployments where silent sign-in feels unexpected to the user.
|
|
22
|
+
* Defaults to `true`.
|
|
23
|
+
*/
|
|
24
|
+
isSSOEnabled?: boolean;
|
|
16
25
|
}
|
|
17
26
|
/**
|
|
18
|
-
* Pages that can be opened
|
|
27
|
+
* Pages that can be opened in a new tab with the current user pre-authenticated.
|
|
28
|
+
* Used by `getOpenURL` from `@authgear/nextjs/server` and by `openPage` / `<UserSettingsButton>`
|
|
29
|
+
* from `@authgear/nextjs/client`.
|
|
19
30
|
*/
|
|
20
31
|
declare enum Page {
|
|
21
32
|
Settings = "/settings"
|
|
22
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* OIDC `prompt` parameter values.
|
|
36
|
+
* Pass to `signIn({ prompt })` or `SignInButton signInOptions={{ prompt }}` to control
|
|
37
|
+
* whether Authgear shows the login form for a specific authentication call.
|
|
38
|
+
*
|
|
39
|
+
* @see https://docs.authgear.com/authentication-and-access/single-sign-on/force-authgear-to-show-login-page
|
|
40
|
+
*/
|
|
41
|
+
declare enum PromptOption {
|
|
42
|
+
/** Always show the login form, even if the user has an active Authgear session. */
|
|
43
|
+
Login = "login",
|
|
44
|
+
/** Never show the login form; return an error if the user is not already authenticated. */
|
|
45
|
+
None = "none"
|
|
46
|
+
}
|
|
23
47
|
declare const DEFAULT_SCOPES: string[];
|
|
24
48
|
declare enum SessionState {
|
|
25
49
|
Unknown = "UNKNOWN",
|
|
@@ -89,4 +113,4 @@ interface OIDCConfiguration {
|
|
|
89
113
|
issuer: string;
|
|
90
114
|
}
|
|
91
115
|
|
|
92
|
-
export { type AuthgearConfig as A, DEFAULT_SCOPES as D, type JWTPayload as J, type OIDCConfiguration as O, Page as P, type Session as S, type TokenResponse as T, type UserInfo as U, type SessionData as
|
|
116
|
+
export { type AuthgearConfig as A, DEFAULT_SCOPES as D, type JWTPayload as J, type OIDCConfiguration as O, Page as P, type Session as S, type TokenResponse as T, type UserInfo as U, PromptOption as a, type SessionData as b, SessionState as c };
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/config.ts","../src/oauth/discovery.ts","../src/session/cookie.ts","../src/oauth/token.ts","../src/session/state.ts"],"sourcesContent":["import { type AuthgearConfig, DEFAULT_SCOPES } from \"./types.js\";\n\nexport function resolveConfig(config: AuthgearConfig): Required<AuthgearConfig> {\n if (!config.endpoint) throw new Error(\"AuthgearConfig: endpoint is required\");\n if (!config.clientID) throw new Error(\"AuthgearConfig: clientID is required\");\n if (!config.redirectURI) throw new Error(\"AuthgearConfig: redirectURI is required\");\n if (!config.sessionSecret || config.sessionSecret.length < 32) {\n throw new Error(\"AuthgearConfig: sessionSecret must be at least 32 characters\");\n }\n\n return {\n endpoint: config.endpoint.replace(/\\/+$/, \"\"),\n clientID: config.clientID,\n redirectURI: config.redirectURI,\n postLogoutRedirectURI: config.postLogoutRedirectURI ?? \"/\",\n scopes: config.scopes ?? DEFAULT_SCOPES,\n sessionSecret: config.sessionSecret,\n cookieName: config.cookieName ?? \"authgear.session\",\n };\n}\n","import type { OIDCConfiguration } from \"../types.js\";\n\nconst cache = new Map<string, { config: OIDCConfiguration; fetchedAt: number }>();\nconst CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour\n\nexport async function fetchOIDCConfiguration(\n endpoint: string,\n): Promise<OIDCConfiguration> {\n const cached = cache.get(endpoint);\n if (cached && Date.now() - cached.fetchedAt < CACHE_TTL_MS) {\n return cached.config;\n }\n\n const url = `${endpoint}/.well-known/openid-configuration`;\n const res = await fetch(url);\n if (!res.ok) {\n throw new Error(`Failed to fetch OIDC configuration from ${url}: ${res.status}`);\n }\n\n const config = (await res.json()) as OIDCConfiguration;\n cache.set(endpoint, { config, fetchedAt: Date.now() });\n return config;\n}\n\n/** Clear cached OIDC configuration (useful for testing) */\nexport function clearOIDCCache(): void {\n cache.clear();\n}\n","import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from \"node:crypto\";\nimport type { SessionData } from \"../types.js\";\n\nconst ALGORITHM = \"aes-256-gcm\";\nconst IV_LENGTH = 12;\nconst AUTH_TAG_LENGTH = 16;\nconst KEY_LENGTH = 32;\nconst SALT = \"authgear-nextjs-session\";\n\nfunction deriveKey(secret: string): Buffer {\n return scryptSync(secret, SALT, KEY_LENGTH);\n}\n\nexport function encryptSession(data: SessionData, secret: string): string {\n const key = deriveKey(secret);\n const iv = randomBytes(IV_LENGTH);\n const cipher = createCipheriv(ALGORITHM, key, iv, { authTagLength: AUTH_TAG_LENGTH });\n\n const json = JSON.stringify(data);\n const encrypted = Buffer.concat([cipher.update(json, \"utf8\"), cipher.final()]);\n const authTag = cipher.getAuthTag();\n\n // Format: base64(iv + authTag + encrypted)\n return Buffer.concat([iv, authTag, encrypted]).toString(\"base64url\");\n}\n\nexport function decryptSession(encrypted: string, secret: string): SessionData | null {\n try {\n const key = deriveKey(secret);\n const buf = Buffer.from(encrypted, \"base64url\");\n\n if (buf.length < IV_LENGTH + AUTH_TAG_LENGTH) return null;\n\n const iv = buf.subarray(0, IV_LENGTH);\n const authTag = buf.subarray(IV_LENGTH, IV_LENGTH + AUTH_TAG_LENGTH);\n const ciphertext = buf.subarray(IV_LENGTH + AUTH_TAG_LENGTH);\n\n const decipher = createDecipheriv(ALGORITHM, key, iv, { authTagLength: AUTH_TAG_LENGTH });\n decipher.setAuthTag(authTag);\n\n const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);\n return JSON.parse(decrypted.toString(\"utf8\")) as SessionData;\n } catch {\n return null;\n }\n}\n\nexport interface CookieOptions {\n name: string;\n value: string;\n httpOnly?: boolean;\n secure?: boolean;\n sameSite?: \"lax\" | \"strict\" | \"none\";\n path?: string;\n maxAge?: number;\n}\n\nexport function buildSessionCookie(\n cookieName: string,\n data: SessionData,\n secret: string,\n): CookieOptions {\n return {\n name: cookieName,\n value: encryptSession(data, secret),\n httpOnly: true,\n secure: process.env.NODE_ENV === \"production\",\n sameSite: \"lax\",\n path: \"/\",\n maxAge: 30 * 24 * 60 * 60, // 30 days\n };\n}\n\nexport function buildClearCookie(cookieName: string): CookieOptions {\n return {\n name: cookieName,\n value: \"\",\n httpOnly: true,\n secure: process.env.NODE_ENV === \"production\",\n sameSite: \"lax\",\n path: \"/\",\n maxAge: 0,\n };\n}\n\nexport function buildPKCECookie(\n data: { codeVerifier: string; state: string; returnTo: string },\n secret: string,\n): CookieOptions {\n const key = deriveKey(secret);\n const iv = randomBytes(IV_LENGTH);\n const cipher = createCipheriv(ALGORITHM, key, iv, { authTagLength: AUTH_TAG_LENGTH });\n\n const json = JSON.stringify(data);\n const encrypted = Buffer.concat([cipher.update(json, \"utf8\"), cipher.final()]);\n const authTag = cipher.getAuthTag();\n\n return {\n name: \"authgear.pkce\",\n value: Buffer.concat([iv, authTag, encrypted]).toString(\"base64url\"),\n httpOnly: true,\n secure: process.env.NODE_ENV === \"production\",\n sameSite: \"lax\",\n path: \"/\",\n maxAge: 600, // 10 minutes\n };\n}\n\nexport function decryptPKCECookie(\n encrypted: string,\n secret: string,\n): { codeVerifier: string; state: string; returnTo: string } | null {\n try {\n const key = deriveKey(secret);\n const buf = Buffer.from(encrypted, \"base64url\");\n\n if (buf.length < IV_LENGTH + AUTH_TAG_LENGTH) return null;\n\n const iv = buf.subarray(0, IV_LENGTH);\n const authTag = buf.subarray(IV_LENGTH, IV_LENGTH + AUTH_TAG_LENGTH);\n const ciphertext = buf.subarray(IV_LENGTH + AUTH_TAG_LENGTH);\n\n const decipher = createDecipheriv(ALGORITHM, key, iv, { authTagLength: AUTH_TAG_LENGTH });\n decipher.setAuthTag(authTag);\n\n const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);\n return JSON.parse(decrypted.toString(\"utf8\"));\n } catch {\n return null;\n }\n}\n","import type { OIDCConfiguration, TokenResponse, AppSessionTokenResponse } from \"../types.js\";\n\nexport interface ExchangeCodeParams {\n code: string;\n codeVerifier: string;\n clientID: string;\n redirectURI: string;\n}\n\nexport interface RefreshTokenParams {\n refreshToken: string;\n clientID: string;\n}\n\nexport async function exchangeCode(\n oidcConfig: OIDCConfiguration,\n params: ExchangeCodeParams,\n): Promise<TokenResponse> {\n const body = new URLSearchParams({\n grant_type: \"authorization_code\",\n code: params.code,\n code_verifier: params.codeVerifier,\n client_id: params.clientID,\n redirect_uri: params.redirectURI,\n });\n\n const res = await fetch(oidcConfig.token_endpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: body.toString(),\n });\n\n if (!res.ok) {\n const error = await res.text();\n throw new Error(`Token exchange failed (${res.status}): ${error}`);\n }\n\n return res.json() as Promise<TokenResponse>;\n}\n\nexport async function refreshAccessToken(\n oidcConfig: OIDCConfiguration,\n params: RefreshTokenParams,\n): Promise<TokenResponse> {\n const body = new URLSearchParams({\n grant_type: \"refresh_token\",\n refresh_token: params.refreshToken,\n client_id: params.clientID,\n });\n\n const res = await fetch(oidcConfig.token_endpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: body.toString(),\n });\n\n if (!res.ok) {\n const error = await res.text();\n throw new Error(`Token refresh failed (${res.status}): ${error}`);\n }\n\n return res.json() as Promise<TokenResponse>;\n}\n\nexport async function getAppSessionToken(\n endpoint: string,\n refreshToken: string,\n): Promise<AppSessionTokenResponse> {\n const res = await fetch(`${endpoint}/oauth2/app_session_token`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refresh_token: refreshToken }),\n });\n\n if (!res.ok) {\n const error = await res.text();\n throw new Error(`Failed to get app session token (${res.status}): ${error}`);\n }\n\n const json = (await res.json()) as { result: AppSessionTokenResponse };\n return json.result;\n}\n\nexport async function revokeToken(\n oidcConfig: OIDCConfiguration,\n token: string,\n): Promise<void> {\n await fetch(oidcConfig.revocation_endpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({ token }).toString(),\n });\n}\n","import { SessionState, type SessionData, type Session } from \"../types.js\";\n\nexport function deriveSessionState(data: SessionData | null): Session {\n if (!data) {\n return {\n state: SessionState.NoSession,\n accessToken: null,\n refreshToken: null,\n idToken: null,\n expiresAt: null,\n user: null,\n };\n }\n\n return {\n state: SessionState.Authenticated,\n accessToken: data.accessToken,\n refreshToken: data.refreshToken,\n idToken: data.idToken,\n expiresAt: data.expiresAt,\n user: null, // User is fetched separately when needed\n };\n}\n\nexport function isTokenExpired(expiresAt: number): boolean {\n // Consider expired 30 seconds early for safety margin\n return Date.now() / 1000 >= expiresAt - 30;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["export interface AuthgearConfig {\n /** Authgear endpoint, e.g. \"https://myapp.authgear.cloud\" */\n endpoint: string;\n /** OAuth client ID */\n clientID: string;\n /** Redirect URI for OAuth callback, e.g. \"http://localhost:3000/api/auth/callback\" */\n redirectURI: string;\n /** Where to redirect after logout */\n postLogoutRedirectURI?: string;\n /** OAuth scopes. Defaults to [\"openid\", \"offline_access\", \"https://authgear.com/scopes/full-userinfo\"] */\n scopes?: string[];\n /** Secret key for encrypting session cookie (min 32 chars) */\n sessionSecret: string;\n /** Session cookie name. Defaults to \"authgear.session\" */\n cookieName?: string;\n}\n\n/**\n * Pages that can be opened via {@link getOpenURL}.\n */\nexport enum Page {\n Settings = \"/settings\",\n}\n\nexport const DEFAULT_SCOPES = [\n \"openid\",\n \"offline_access\",\n \"https://authgear.com/scopes/full-userinfo\",\n];\n\nexport enum SessionState {\n Unknown = \"UNKNOWN\",\n NoSession = \"NO_SESSION\",\n Authenticated = \"AUTHENTICATED\",\n}\n\nexport interface SessionData {\n accessToken: string;\n refreshToken: string | null;\n idToken: string | null;\n expiresAt: number;\n}\n\nexport interface Session {\n state: SessionState;\n accessToken: string | null;\n refreshToken: string | null;\n idToken: string | null;\n expiresAt: number | null;\n user: UserInfo | null;\n}\n\nexport interface UserInfo {\n sub: string;\n email?: string;\n emailVerified?: boolean;\n phoneNumber?: string;\n phoneNumberVerified?: boolean;\n preferredUsername?: string;\n givenName?: string;\n familyName?: string;\n name?: string;\n picture?: string;\n roles?: string[];\n isAnonymous?: boolean;\n isVerified?: boolean;\n canReauthenticate?: boolean;\n customAttributes?: Record<string, unknown>;\n raw: Record<string, unknown>;\n}\n\nexport interface JWTPayload {\n sub: string;\n iss: string;\n aud: string | string[];\n exp: number;\n iat: number;\n jti?: string;\n client_id?: string;\n \"https://authgear.com/claims/user/is_anonymous\"?: boolean;\n \"https://authgear.com/claims/user/is_verified\"?: boolean;\n \"https://authgear.com/claims/user/can_reauthenticate\"?: boolean;\n \"https://authgear.com/claims/user/roles\"?: string[];\n [key: string]: unknown;\n}\n\nexport interface TokenResponse {\n access_token: string;\n token_type: string;\n expires_in: number;\n refresh_token?: string;\n id_token?: string;\n}\n\nexport interface AppSessionTokenResponse {\n app_session_token: string;\n expire_at: string;\n}\n\nexport interface OIDCConfiguration {\n authorization_endpoint: string;\n token_endpoint: string;\n userinfo_endpoint: string;\n revocation_endpoint: string;\n end_session_endpoint: string;\n jwks_uri: string;\n issuer: string;\n}\n"],"mappings":";AAoBO,IAAK,OAAL,kBAAKA,UAAL;AACL,EAAAA,MAAA,cAAW;AADD,SAAAA;AAAA,GAAA;AAIL,IAAM,iBAAiB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAK,eAAL,kBAAKC,kBAAL;AACL,EAAAA,cAAA,aAAU;AACV,EAAAA,cAAA,eAAY;AACZ,EAAAA,cAAA,mBAAgB;AAHN,SAAAA;AAAA,GAAA;","names":["Page","SessionState"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/oauth/authorize.ts","../src/oauth/pkce.ts","../src/user.ts"],"sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport type { OIDCConfiguration } from \"../types.js\";\nimport { computeCodeChallenge } from \"./pkce.js\";\n\n/**\n * Build the URL to open an Authgear page (e.g. /settings) with the user\n * already authenticated via an app session token.\n */\nexport function buildOpenURL(\n oidcConfig: OIDCConfiguration,\n params: {\n clientID: string;\n appSessionToken: string;\n targetPath: string; // e.g. \"/settings\"\n scopes: string[];\n },\n): string {\n const authorizationEndpoint = new URL(oidcConfig.authorization_endpoint);\n const settingsURL = `${authorizationEndpoint.origin}${params.targetPath}`;\n const loginHint = `https://authgear.com/login_hint?type=app_session_token&app_session_token=${encodeURIComponent(params.appSessionToken)}`;\n\n const url = new URL(oidcConfig.authorization_endpoint);\n url.searchParams.set(\"response_type\", \"none\");\n url.searchParams.set(\"client_id\", params.clientID);\n url.searchParams.set(\"redirect_uri\", settingsURL);\n url.searchParams.set(\"scope\", params.scopes.join(\" \"));\n url.searchParams.set(\"prompt\", \"none\");\n url.searchParams.set(\"login_hint\", loginHint);\n return url.toString();\n}\n\nexport interface AuthorizeParams {\n clientID: string;\n redirectURI: string;\n scopes: string[];\n codeVerifier: string;\n state: string;\n}\n\nexport function generateState(): string {\n return randomBytes(32).toString(\"base64url\");\n}\n\nexport function buildAuthorizeURL(\n oidcConfig: OIDCConfiguration,\n params: AuthorizeParams,\n): string {\n const url = new URL(oidcConfig.authorization_endpoint);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"client_id\", params.clientID);\n url.searchParams.set(\"redirect_uri\", params.redirectURI);\n url.searchParams.set(\"scope\", params.scopes.join(\" \"));\n url.searchParams.set(\"code_challenge\", computeCodeChallenge(params.codeVerifier));\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n url.searchParams.set(\"state\", params.state);\n return url.toString();\n}\n","import { randomBytes, createHash } from \"node:crypto\";\n\nconst VERIFIER_LENGTH = 64;\n\nexport function generateCodeVerifier(): string {\n return randomBytes(VERIFIER_LENGTH)\n .toString(\"base64url\")\n .slice(0, VERIFIER_LENGTH);\n}\n\nexport function computeCodeChallenge(codeVerifier: string): string {\n return createHash(\"sha256\").update(codeVerifier).digest(\"base64url\");\n}\n","import type { UserInfo } from \"./types.js\";\n\nexport function parseUserInfo(raw: Record<string, unknown>): UserInfo {\n return {\n sub: raw[\"sub\"] as string,\n email: raw[\"email\"] as string | undefined,\n emailVerified: raw[\"email_verified\"] as boolean | undefined,\n phoneNumber: raw[\"phone_number\"] as string | undefined,\n phoneNumberVerified: raw[\"phone_number_verified\"] as boolean | undefined,\n preferredUsername: raw[\"preferred_username\"] as string | undefined,\n givenName: raw[\"given_name\"] as string | undefined,\n familyName: raw[\"family_name\"] as string | undefined,\n name: raw[\"name\"] as string | undefined,\n picture: raw[\"picture\"] as string | undefined,\n roles: raw[\"https://authgear.com/claims/user/roles\"] as string[] | undefined,\n isAnonymous: raw[\"https://authgear.com/claims/user/is_anonymous\"] as boolean | undefined,\n isVerified: raw[\"https://authgear.com/claims/user/is_verified\"] as boolean | undefined,\n canReauthenticate: raw[\"https://authgear.com/claims/user/can_reauthenticate\"] as boolean | undefined,\n customAttributes: raw[\"custom_attributes\"] as Record<string, unknown> | undefined,\n raw,\n };\n}\n"],"mappings":";AAAA,SAAS,eAAAA,oBAAmB;;;ACA5B,SAAS,aAAa,kBAAkB;AAExC,IAAM,kBAAkB;AAEjB,SAAS,uBAA+B;AAC7C,SAAO,YAAY,eAAe,EAC/B,SAAS,WAAW,EACpB,MAAM,GAAG,eAAe;AAC7B;AAEO,SAAS,qBAAqB,cAA8B;AACjE,SAAO,WAAW,QAAQ,EAAE,OAAO,YAAY,EAAE,OAAO,WAAW;AACrE;;;ADJO,SAAS,aACd,YACA,QAMQ;AACR,QAAM,wBAAwB,IAAI,IAAI,WAAW,sBAAsB;AACvE,QAAM,cAAc,GAAG,sBAAsB,MAAM,GAAG,OAAO,UAAU;AACvE,QAAM,YAAY,4EAA4E,mBAAmB,OAAO,eAAe,CAAC;AAExI,QAAM,MAAM,IAAI,IAAI,WAAW,sBAAsB;AACrD,MAAI,aAAa,IAAI,iBAAiB,MAAM;AAC5C,MAAI,aAAa,IAAI,aAAa,OAAO,QAAQ;AACjD,MAAI,aAAa,IAAI,gBAAgB,WAAW;AAChD,MAAI,aAAa,IAAI,SAAS,OAAO,OAAO,KAAK,GAAG,CAAC;AACrD,MAAI,aAAa,IAAI,UAAU,MAAM;AACrC,MAAI,aAAa,IAAI,cAAc,SAAS;AAC5C,SAAO,IAAI,SAAS;AACtB;AAUO,SAAS,gBAAwB;AACtC,SAAOC,aAAY,EAAE,EAAE,SAAS,WAAW;AAC7C;AAEO,SAAS,kBACd,YACA,QACQ;AACR,QAAM,MAAM,IAAI,IAAI,WAAW,sBAAsB;AACrD,MAAI,aAAa,IAAI,iBAAiB,MAAM;AAC5C,MAAI,aAAa,IAAI,aAAa,OAAO,QAAQ;AACjD,MAAI,aAAa,IAAI,gBAAgB,OAAO,WAAW;AACvD,MAAI,aAAa,IAAI,SAAS,OAAO,OAAO,KAAK,GAAG,CAAC;AACrD,MAAI,aAAa,IAAI,kBAAkB,qBAAqB,OAAO,YAAY,CAAC;AAChF,MAAI,aAAa,IAAI,yBAAyB,MAAM;AACpD,MAAI,aAAa,IAAI,SAAS,OAAO,KAAK;AAC1C,SAAO,IAAI,SAAS;AACtB;;;AEtDO,SAAS,cAAc,KAAwC;AACpE,SAAO;AAAA,IACL,KAAK,IAAI,KAAK;AAAA,IACd,OAAO,IAAI,OAAO;AAAA,IAClB,eAAe,IAAI,gBAAgB;AAAA,IACnC,aAAa,IAAI,cAAc;AAAA,IAC/B,qBAAqB,IAAI,uBAAuB;AAAA,IAChD,mBAAmB,IAAI,oBAAoB;AAAA,IAC3C,WAAW,IAAI,YAAY;AAAA,IAC3B,YAAY,IAAI,aAAa;AAAA,IAC7B,MAAM,IAAI,MAAM;AAAA,IAChB,SAAS,IAAI,SAAS;AAAA,IACtB,OAAO,IAAI,wCAAwC;AAAA,IACnD,aAAa,IAAI,+CAA+C;AAAA,IAChE,YAAY,IAAI,8CAA8C;AAAA,IAC9D,mBAAmB,IAAI,qDAAqD;AAAA,IAC5E,kBAAkB,IAAI,mBAAmB;AAAA,IACzC;AAAA,EACF;AACF;","names":["randomBytes","randomBytes"]}
|