@lpshanley/oidc-start-core 0.0.0-stage → 0.1.0-next.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 +24 -2
- package/dist/config.d.ts +61 -0
- package/dist/config.js +71 -0
- package/dist/cookies.d.ts +21 -0
- package/dist/cookies.js +93 -0
- package/dist/core.d.ts +123 -0
- package/dist/core.js +352 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +1 -0
- package/dist/logout-token.d.ts +16 -0
- package/dist/logout-token.js +36 -0
- package/dist/models.d.ts +29 -0
- package/dist/models.js +57 -0
- package/dist/provider.d.ts +49 -0
- package/dist/provider.js +242 -0
- package/dist/revocation.d.ts +19 -0
- package/dist/revocation.js +1 -0
- package/dist/seal.d.ts +4 -0
- package/dist/seal.js +36 -0
- package/dist/session.d.ts +58 -0
- package/dist/session.js +141 -0
- package/dist/state.d.ts +50 -0
- package/dist/state.js +84 -0
- package/package.json +56 -3
package/README.md
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @lpshanley/oidc-start-core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
OpenID Connect for a Node.js server. Handles discovery, code exchange with PKCE, sealed browser sessions, refresh, revocation and back-channel logout. Use the separate `@lpshanley/oidc-start-react` package for TanStack Start.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { createOidcCore } from '@lpshanley/oidc-start-core'
|
|
7
|
+
import { hasRole } from '@lpshanley/oidc-start-core/models'
|
|
8
|
+
|
|
9
|
+
const auth = createOidcCore({
|
|
10
|
+
cookiePrefix: 'myapp',
|
|
11
|
+
callbackPath: '/auth/callback',
|
|
12
|
+
postLogoutPath: '/signed-out',
|
|
13
|
+
allowSession: user => hasRole(user, 'members'),
|
|
14
|
+
revocations: mySessionRevocationStore,
|
|
15
|
+
})
|
|
16
|
+
auth.assertConfigured()
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Provide `OIDC_ISSUER`, `OIDC_CLIENT_ID`, `OIDC_CLIENT_SECRET`, and a `SESSION_SECRET` of at least 32 characters. Set `APP_ORIGIN` to the public app origin and `OIDC_ROLES_CLAIM` to the provider's role claim (default `roles`). Register the callback and post-logout URLs. Each app chooses its own role policy and supplies a `SessionRevocations` implementation shared by its server instances. The core checks that policy at sign-in, on session access and after refresh.
|
|
20
|
+
|
|
21
|
+
Back-channel logout requires a signed token with an expiration (`exp`) and a nonempty token ID (`jti`). The signed issue time sets the revocation cutoff, in epoch seconds. For `revokeAllFor` and `revokeIdpSession`, atomically keep the greater of the stored and incoming timestamp so an older delivery cannot undo a newer revocation. A subject cutoff includes sessions issued in the same second.
|
|
22
|
+
|
|
23
|
+
Use `startGrant` and `finishGrant` on the server to obtain an independent provider grant. Register a separate `grantCallbackPath` (default `/api/auth/grant-callback`). The sealed state binds each flow to its purpose. The host checks the returned subject and permissions, stores the credentials and decides which client can use them. `refreshGrant` returns `refused` for a provider rejection of the grant. Other errors propagate. A `RefreshNotDispatched` from `/provider` means the refresh request was never sent.
|
|
24
|
+
|
|
25
|
+
The `/models` entry is safe to import in browser code. Credentials and session management belong on the server.
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { AuthUser } from "./models.js";
|
|
3
|
+
import type { SessionRevocations } from "./revocation.js";
|
|
4
|
+
export type AuthEnvironment = Readonly<Record<string, string | undefined>>;
|
|
5
|
+
export type AuthEnvironmentProvider = () => AuthEnvironment;
|
|
6
|
+
export declare const authServiceOptionsSchema: z.ZodObject<{
|
|
7
|
+
cookiePrefix: z.ZodDefault<z.ZodString>;
|
|
8
|
+
sessionMaxAgeSeconds: z.ZodDefault<z.ZodNumber>;
|
|
9
|
+
sessionIdleSeconds: z.ZodDefault<z.ZodNumber>;
|
|
10
|
+
callbackPath: z.ZodDefault<z.ZodString>;
|
|
11
|
+
grantCallbackPath: z.ZodDefault<z.ZodString>;
|
|
12
|
+
postLogoutPath: z.ZodDefault<z.ZodString>;
|
|
13
|
+
providerTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
14
|
+
}, z.core.$strip>;
|
|
15
|
+
export type AuthServiceOptions = z.input<typeof authServiceOptionsSchema>;
|
|
16
|
+
export declare const sessionConfigurationSchema: z.ZodObject<{
|
|
17
|
+
SESSION_SECRET: z.ZodString;
|
|
18
|
+
}, z.core.$strip>;
|
|
19
|
+
export declare const oidcConfigurationSchema: z.ZodObject<{
|
|
20
|
+
OIDC_ISSUER: z.ZodPipe<z.ZodString, z.ZodURL>;
|
|
21
|
+
OIDC_CLIENT_ID: z.ZodString;
|
|
22
|
+
OIDC_CLIENT_SECRET: z.ZodString;
|
|
23
|
+
OIDC_SCOPES: z.ZodPreprocess<z.ZodDefault<z.ZodString>, unknown>;
|
|
24
|
+
OIDC_ROLES_CLAIM: z.ZodPreprocess<z.ZodDefault<z.ZodString>, unknown>;
|
|
25
|
+
}, z.core.$strip>;
|
|
26
|
+
export type OidcConfiguration = z.output<typeof oidcConfigurationSchema>;
|
|
27
|
+
export declare const authOriginConfigurationSchema: z.ZodObject<{
|
|
28
|
+
APP_ORIGIN: z.ZodPreprocess<z.ZodOptional<z.ZodURL>, unknown>;
|
|
29
|
+
OIDC_REDIRECT_URI: z.ZodCatch<z.ZodOptional<z.ZodURL>>;
|
|
30
|
+
NODE_ENV: z.ZodOptional<z.ZodString>;
|
|
31
|
+
}, z.core.$strip>;
|
|
32
|
+
export declare function assertOidcConfigured(env: AuthEnvironment): void;
|
|
33
|
+
/**
|
|
34
|
+
* The caller's request context, read from `context.requestContext` when an
|
|
35
|
+
* earlier request middleware provides one.
|
|
36
|
+
*/
|
|
37
|
+
export type OidcCallContext = {
|
|
38
|
+
correlationId?: string;
|
|
39
|
+
deadline?: number;
|
|
40
|
+
signal?: AbortSignal;
|
|
41
|
+
};
|
|
42
|
+
/** A failure the library recovered from or reported to the browser. */
|
|
43
|
+
export type OidcErrorEvent = {
|
|
44
|
+
operation: "sign-in" | "callback" | "refresh" | "end-session" | "token-revocation" | "backchannel-logout";
|
|
45
|
+
error: unknown;
|
|
46
|
+
context?: OidcCallContext;
|
|
47
|
+
};
|
|
48
|
+
export type OidcMiddlewareOptions = AuthServiceOptions & {
|
|
49
|
+
/** The host decides who may start or keep a session. */
|
|
50
|
+
allowSession: (user: AuthUser) => boolean;
|
|
51
|
+
/** Server-side record of ended sessions. */
|
|
52
|
+
revocations: SessionRevocations;
|
|
53
|
+
/** Read configuration inside each request. Defaults to process.env. */
|
|
54
|
+
environment?: AuthEnvironmentProvider;
|
|
55
|
+
/**
|
|
56
|
+
* Receives provider and flow failures. The error may carry provider
|
|
57
|
+
* responses, so redact it before logging. Defaults to logging the
|
|
58
|
+
* operation and request ID only.
|
|
59
|
+
*/
|
|
60
|
+
onError?: (event: OidcErrorEvent) => void;
|
|
61
|
+
};
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const authServiceOptionsSchema = z.object({
|
|
3
|
+
/** Cookie names start with this prefix. Defaults to `oidc`. */
|
|
4
|
+
cookiePrefix: z
|
|
5
|
+
.string()
|
|
6
|
+
.regex(/^[A-Za-z0-9_-]+$/, "cookiePrefix must contain only letters, numbers, underscores or hyphens")
|
|
7
|
+
.default("oidc"),
|
|
8
|
+
/** Application session lifetime in seconds. Defaults to 14 days. */
|
|
9
|
+
sessionMaxAgeSeconds: z
|
|
10
|
+
.number()
|
|
11
|
+
.int("sessionMaxAgeSeconds must be a positive integer")
|
|
12
|
+
.positive("sessionMaxAgeSeconds must be a positive integer")
|
|
13
|
+
.default(60 * 60 * 24 * 14),
|
|
14
|
+
/** Session lifetime without requests, in seconds. Defaults to 12 hours. */
|
|
15
|
+
sessionIdleSeconds: z
|
|
16
|
+
.number()
|
|
17
|
+
.int()
|
|
18
|
+
.positive()
|
|
19
|
+
.default(60 * 60 * 12),
|
|
20
|
+
/** Callback path when OIDC_REDIRECT_URI is unset. */
|
|
21
|
+
callbackPath: z.string().default("/api/auth/callback"),
|
|
22
|
+
/** Separate return address for app-managed grants. */
|
|
23
|
+
grantCallbackPath: z.string().default("/api/auth/grant-callback"),
|
|
24
|
+
/** Application path after provider logout. Defaults to `/login`. */
|
|
25
|
+
postLogoutPath: z.string().default("/login"),
|
|
26
|
+
/** Timeout for each provider request. Defaults to 5 seconds. */
|
|
27
|
+
providerTimeoutMs: z.number().int().positive().default(5_000),
|
|
28
|
+
});
|
|
29
|
+
export const sessionConfigurationSchema = z.object({
|
|
30
|
+
SESSION_SECRET: z
|
|
31
|
+
.string({ error: "Missing required environment variable: SESSION_SECRET" })
|
|
32
|
+
.min(1, "Missing required environment variable: SESSION_SECRET")
|
|
33
|
+
.min(32, "SESSION_SECRET must be at least 32 characters"),
|
|
34
|
+
});
|
|
35
|
+
export const oidcConfigurationSchema = z.object({
|
|
36
|
+
OIDC_ISSUER: z
|
|
37
|
+
.string({
|
|
38
|
+
error: "Missing required environment variable: OIDC_ISSUER",
|
|
39
|
+
})
|
|
40
|
+
.min(1, "Missing required environment variable: OIDC_ISSUER")
|
|
41
|
+
.pipe(z.url({ error: "OIDC_ISSUER must be a valid URL" })),
|
|
42
|
+
OIDC_CLIENT_ID: z
|
|
43
|
+
.string({
|
|
44
|
+
error: "Missing required environment variable: OIDC_CLIENT_ID",
|
|
45
|
+
})
|
|
46
|
+
.min(1, "Missing required environment variable: OIDC_CLIENT_ID"),
|
|
47
|
+
OIDC_CLIENT_SECRET: z
|
|
48
|
+
.string({
|
|
49
|
+
error: "Missing required environment variable: OIDC_CLIENT_SECRET",
|
|
50
|
+
})
|
|
51
|
+
.min(1, "Missing required environment variable: OIDC_CLIENT_SECRET"),
|
|
52
|
+
/** Space-separated scopes; offline_access requests a refresh token. */
|
|
53
|
+
OIDC_SCOPES: z.preprocess((value) => (value === "" ? undefined : value), z.string().default("openid profile email offline_access")),
|
|
54
|
+
/** The claim holding the user's roles, read into `AuthUser.roles`. */
|
|
55
|
+
OIDC_ROLES_CLAIM: z.preprocess((value) => (value === "" ? undefined : value), z.string().default("roles")),
|
|
56
|
+
});
|
|
57
|
+
export const authOriginConfigurationSchema = z.object({
|
|
58
|
+
APP_ORIGIN: z.preprocess((value) => (value === "" ? undefined : value), z.url({ error: "APP_ORIGIN must be a valid URL" }).optional()),
|
|
59
|
+
OIDC_REDIRECT_URI: z.url().optional().catch(undefined),
|
|
60
|
+
NODE_ENV: z.string().optional(),
|
|
61
|
+
});
|
|
62
|
+
export function assertOidcConfigured(env) {
|
|
63
|
+
const issues = [
|
|
64
|
+
oidcConfigurationSchema,
|
|
65
|
+
sessionConfigurationSchema,
|
|
66
|
+
authOriginConfigurationSchema,
|
|
67
|
+
].flatMap((schema) => schema.safeParse(env).error?.issues ?? []);
|
|
68
|
+
if (issues.length) {
|
|
69
|
+
throw new Error(`Invalid OIDC configuration: ${issues.map((issue) => issue.message).join("; ")}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type CookieOptions = {
|
|
2
|
+
path?: string;
|
|
3
|
+
httpOnly?: boolean;
|
|
4
|
+
secure?: boolean;
|
|
5
|
+
sameSite?: "lax" | "strict" | "none";
|
|
6
|
+
maxAge?: number;
|
|
7
|
+
};
|
|
8
|
+
/** Browsers cap a cookie near 4096 bytes; leave room for name and attributes. */
|
|
9
|
+
export declare const CHUNK_SIZE = 3800;
|
|
10
|
+
export declare function parseCookies(header: string | null | undefined): Map<string, string>;
|
|
11
|
+
export declare function serializeCookie(name: string, value: string, options: CookieOptions): string;
|
|
12
|
+
/** Reads `name`, or its contiguous `name.0`, `name.1`, … chunks. */
|
|
13
|
+
export declare function readChunked(cookies: Map<string, string>, name: string): string | undefined;
|
|
14
|
+
/** Set-Cookie lines that delete `name` and every chunk of it. */
|
|
15
|
+
export declare function clearChunked(cookies: Map<string, string>, name: string, options: CookieOptions): string[];
|
|
16
|
+
/**
|
|
17
|
+
* Set-Cookie lines that store `value` under `name`, splitting it into
|
|
18
|
+
* `name.0..n` chunks when it is too large for one cookie, and deleting any
|
|
19
|
+
* stale cookie or chunk left from a previous size.
|
|
20
|
+
*/
|
|
21
|
+
export declare function writeChunked(cookies: Map<string, string>, name: string, value: string, options: CookieOptions): string[];
|
package/dist/cookies.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/** Browsers cap a cookie near 4096 bytes; leave room for name and attributes. */
|
|
2
|
+
export const CHUNK_SIZE = 3800;
|
|
3
|
+
export function parseCookies(header) {
|
|
4
|
+
const cookies = new Map();
|
|
5
|
+
for (const part of header?.split(";") ?? []) {
|
|
6
|
+
const index = part.indexOf("=");
|
|
7
|
+
if (index < 1)
|
|
8
|
+
continue;
|
|
9
|
+
const name = part.slice(0, index).trim();
|
|
10
|
+
const raw = part.slice(index + 1).trim();
|
|
11
|
+
try {
|
|
12
|
+
cookies.set(name, decodeURIComponent(raw));
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
cookies.set(name, raw);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return cookies;
|
|
19
|
+
}
|
|
20
|
+
export function serializeCookie(name, value, options) {
|
|
21
|
+
const parts = [`${name}=${encodeURIComponent(value)}`];
|
|
22
|
+
if (options.maxAge !== undefined)
|
|
23
|
+
parts.push(`Max-Age=${options.maxAge}`);
|
|
24
|
+
parts.push(`Path=${options.path ?? "/"}`);
|
|
25
|
+
if (options.httpOnly)
|
|
26
|
+
parts.push("HttpOnly");
|
|
27
|
+
if (options.secure)
|
|
28
|
+
parts.push("Secure");
|
|
29
|
+
if (options.sameSite) {
|
|
30
|
+
parts.push(`SameSite=${options.sameSite[0]?.toUpperCase()}${options.sameSite.slice(1)}`);
|
|
31
|
+
}
|
|
32
|
+
return parts.join("; ");
|
|
33
|
+
}
|
|
34
|
+
function chunkNames(cookies, name) {
|
|
35
|
+
const pattern = new RegExp(`^${name.replace(/[.-]/g, "\\$&")}\\.(\\d+)$`);
|
|
36
|
+
return [...cookies.keys()]
|
|
37
|
+
.map((key) => ({ key, index: Number(pattern.exec(key)?.[1]) }))
|
|
38
|
+
.filter(({ index }) => Number.isInteger(index))
|
|
39
|
+
.sort((a, b) => a.index - b.index);
|
|
40
|
+
}
|
|
41
|
+
/** Reads `name`, or its contiguous `name.0`, `name.1`, … chunks. */
|
|
42
|
+
export function readChunked(cookies, name) {
|
|
43
|
+
const whole = cookies.get(name);
|
|
44
|
+
if (whole !== undefined)
|
|
45
|
+
return whole;
|
|
46
|
+
const chunks = chunkNames(cookies, name);
|
|
47
|
+
if (chunks.length === 0 || chunks.some(({ index }, i) => index !== i)) {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
return chunks.map(({ key }) => cookies.get(key)).join("");
|
|
51
|
+
}
|
|
52
|
+
/** Set-Cookie lines that delete `name` and every chunk of it. */
|
|
53
|
+
export function clearChunked(cookies, name, options) {
|
|
54
|
+
const expired = { ...options, maxAge: 0 };
|
|
55
|
+
const keys = [
|
|
56
|
+
...(cookies.has(name) ? [name] : []),
|
|
57
|
+
...chunkNames(cookies, name).map(({ key }) => key),
|
|
58
|
+
];
|
|
59
|
+
return keys.map((key) => serializeCookie(key, "", expired));
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Set-Cookie lines that store `value` under `name`, splitting it into
|
|
63
|
+
* `name.0..n` chunks when it is too large for one cookie, and deleting any
|
|
64
|
+
* stale cookie or chunk left from a previous size.
|
|
65
|
+
*/
|
|
66
|
+
export function writeChunked(cookies, name, value, options) {
|
|
67
|
+
const encoded = encodeURIComponent(value);
|
|
68
|
+
if (encoded.length <= CHUNK_SIZE) {
|
|
69
|
+
return [
|
|
70
|
+
serializeCookie(name, value, options),
|
|
71
|
+
...clearChunked(new Map(chunkNames(cookies, name).map(({ key }) => [key, ""])), name, options),
|
|
72
|
+
];
|
|
73
|
+
}
|
|
74
|
+
// Split the plain value so no percent-escape is cut in half.
|
|
75
|
+
const chunks = [];
|
|
76
|
+
let current = "";
|
|
77
|
+
for (const character of value) {
|
|
78
|
+
if (encodeURIComponent(current + character).length > CHUNK_SIZE) {
|
|
79
|
+
chunks.push(current);
|
|
80
|
+
current = "";
|
|
81
|
+
}
|
|
82
|
+
current += character;
|
|
83
|
+
}
|
|
84
|
+
if (current)
|
|
85
|
+
chunks.push(current);
|
|
86
|
+
const written = chunks.map((chunk, index) => serializeCookie(`${name}.${index}`, chunk, options));
|
|
87
|
+
const stale = clearChunked(cookies, name, options).filter((line) => {
|
|
88
|
+
const key = line.slice(0, line.indexOf("="));
|
|
89
|
+
const index = Number(key.slice(name.length + 1));
|
|
90
|
+
return key === name || index >= chunks.length;
|
|
91
|
+
});
|
|
92
|
+
return [...written, ...stale];
|
|
93
|
+
}
|
package/dist/core.d.ts
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { type OidcCallContext, type OidcErrorEvent, type OidcMiddlewareOptions } from "./config.js";
|
|
2
|
+
import { type AuthUser } from "./models.js";
|
|
3
|
+
import { type TokenSet } from "./provider.js";
|
|
4
|
+
import { type Session } from "./session.js";
|
|
5
|
+
export type OidcAuth = {
|
|
6
|
+
user: AuthUser | null;
|
|
7
|
+
};
|
|
8
|
+
/** The caller's deadline passed before the provider answered. */
|
|
9
|
+
export declare class OidcDeadlineError extends Error {
|
|
10
|
+
name: string;
|
|
11
|
+
constructor();
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Settles with the work, or rejects once the caller's deadline passes. Each
|
|
15
|
+
* provider request also has its own timeout, so abandoned work still ends.
|
|
16
|
+
*/
|
|
17
|
+
export declare function withinDeadline<T>(work: Promise<T>, context?: OidcCallContext, now?: number): Promise<T>;
|
|
18
|
+
/** The provider authenticated a user whom the host denied a session. */
|
|
19
|
+
export declare class SessionAccessDeniedError extends Error {
|
|
20
|
+
name: string;
|
|
21
|
+
constructor();
|
|
22
|
+
}
|
|
23
|
+
export type CallbackResult = {
|
|
24
|
+
ok: true;
|
|
25
|
+
location: string;
|
|
26
|
+
setCookies: string[];
|
|
27
|
+
} | {
|
|
28
|
+
ok: false;
|
|
29
|
+
error: unknown;
|
|
30
|
+
setCookies: string[];
|
|
31
|
+
};
|
|
32
|
+
export type GrantResult = {
|
|
33
|
+
ok: true;
|
|
34
|
+
user: AuthUser;
|
|
35
|
+
tokens: TokenSet;
|
|
36
|
+
returnPathname: string;
|
|
37
|
+
setCookies: string[];
|
|
38
|
+
} | {
|
|
39
|
+
ok: false;
|
|
40
|
+
error: unknown;
|
|
41
|
+
setCookies: string[];
|
|
42
|
+
};
|
|
43
|
+
export type GrantRefresh = {
|
|
44
|
+
status: "refreshed";
|
|
45
|
+
tokens: TokenSet;
|
|
46
|
+
} | {
|
|
47
|
+
status: "refused";
|
|
48
|
+
};
|
|
49
|
+
export declare function createOidcCore(options: OidcMiddlewareOptions): {
|
|
50
|
+
/** Reads, and when needed refreshes or clears, the request's session. */
|
|
51
|
+
resolve(request: Request, context?: OidcCallContext): Promise<{
|
|
52
|
+
cookies: Map<string, string>;
|
|
53
|
+
session: {
|
|
54
|
+
user: {
|
|
55
|
+
sub: string;
|
|
56
|
+
email: string;
|
|
57
|
+
name: string | null;
|
|
58
|
+
picture?: string | null | undefined;
|
|
59
|
+
roles?: string[] | undefined;
|
|
60
|
+
};
|
|
61
|
+
accessToken: string;
|
|
62
|
+
expiresAt: number;
|
|
63
|
+
sid: string;
|
|
64
|
+
issuedAt: number;
|
|
65
|
+
lastSeenAt: number;
|
|
66
|
+
refreshToken?: string | undefined;
|
|
67
|
+
idToken?: string | undefined;
|
|
68
|
+
idpSid?: string | undefined;
|
|
69
|
+
} | null;
|
|
70
|
+
setCookies: string[];
|
|
71
|
+
}>;
|
|
72
|
+
/** An authorization URL plus the flow's verifier cookie. */
|
|
73
|
+
signIn(request: Request, cookies: Map<string, string>, returnPathname?: string | null, context?: OidcCallContext): Promise<{
|
|
74
|
+
url: string;
|
|
75
|
+
setCookies: string[];
|
|
76
|
+
}>;
|
|
77
|
+
/** Verifies the flow, exchanges the code, and issues the session. */
|
|
78
|
+
callback(request: Request, cookies: Map<string, string>, context?: OidcCallContext): Promise<CallbackResult>;
|
|
79
|
+
/**
|
|
80
|
+
* Starts a grant: an authorization like sign-in whose tokens the app
|
|
81
|
+
* keeps for itself, returning on `grantCallbackPath`. It writes no
|
|
82
|
+
* session, so the browser's own session is untouched.
|
|
83
|
+
*/
|
|
84
|
+
startGrant(request: Request, cookies: Map<string, string>, returnPathname: string, context?: OidcCallContext): Promise<{
|
|
85
|
+
url: string;
|
|
86
|
+
setCookies: string[];
|
|
87
|
+
}>;
|
|
88
|
+
/** Verifies a grant's callback and exchanges its code for tokens. */
|
|
89
|
+
finishGrant(request: Request, cookies: Map<string, string>, context?: OidcCallContext): Promise<GrantResult>;
|
|
90
|
+
/**
|
|
91
|
+
* Refreshes a grant's tokens. `refused` means the provider ended the
|
|
92
|
+
* grant (`invalid_grant`); any other failure throws, since the provider
|
|
93
|
+
* may only be unreachable.
|
|
94
|
+
*/
|
|
95
|
+
refreshGrant(refreshToken: string, context?: OidcCallContext): Promise<GrantRefresh>;
|
|
96
|
+
/**
|
|
97
|
+
* Ends the session server-side (every session of the user with
|
|
98
|
+
* `everywhere`), clears the cookie, and returns the provider logout URL.
|
|
99
|
+
* Throws when the revocation cannot be recorded, so a sign-out is never
|
|
100
|
+
* reported done while the cookie would still work.
|
|
101
|
+
*/
|
|
102
|
+
signOut(request: Request, cookies: Map<string, string>, session: Session | null, context?: OidcCallContext, everywhere?: boolean): Promise<{
|
|
103
|
+
location: string;
|
|
104
|
+
setCookies: string[];
|
|
105
|
+
}>;
|
|
106
|
+
/**
|
|
107
|
+
* Handles a provider's back-channel logout: verifies the posted
|
|
108
|
+
* logout_token and ends the sessions it names. Answers 400 for an
|
|
109
|
+
* invalid token and 503 when the provider's keys or the revocation
|
|
110
|
+
* store cannot be reached, so the provider can retry.
|
|
111
|
+
*/
|
|
112
|
+
backchannelLogout(request: Request, context?: OidcCallContext): Promise<{
|
|
113
|
+
status: 200 | 400 | 503;
|
|
114
|
+
}>;
|
|
115
|
+
/** Reports a failure through the configured `onError`. */
|
|
116
|
+
report: (event: OidcErrorEvent) => void;
|
|
117
|
+
/**
|
|
118
|
+
* Throws one error naming every missing or invalid setting, so the
|
|
119
|
+
* app can refuse to serve before the first sign-in needs them.
|
|
120
|
+
*/
|
|
121
|
+
assertConfigured(): void;
|
|
122
|
+
};
|
|
123
|
+
export type OidcCore = ReturnType<typeof createOidcCore>;
|