@civic/auth 0.0.1-beta.18 → 0.0.1-beta.2

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.
Files changed (52) hide show
  1. package/README.md +0 -26
  2. package/dist/chunk-5NUJ7LFF.mjs +17 -0
  3. package/dist/chunk-5NUJ7LFF.mjs.map +1 -0
  4. package/dist/chunk-KS7ERXGZ.js +481 -0
  5. package/dist/chunk-KS7ERXGZ.js.map +1 -0
  6. package/dist/chunk-NINRO7GS.js +209 -0
  7. package/dist/chunk-NINRO7GS.js.map +1 -0
  8. package/dist/chunk-NXBKSUKI.mjs +481 -0
  9. package/dist/chunk-NXBKSUKI.mjs.map +1 -0
  10. package/dist/chunk-T7HUHQ3J.mjs +209 -0
  11. package/dist/chunk-T7HUHQ3J.mjs.map +1 -0
  12. package/dist/chunk-WZLC5B4C.js +17 -0
  13. package/dist/chunk-WZLC5B4C.js.map +1 -0
  14. package/dist/index-DoDoIY_K.d.mts +79 -0
  15. package/dist/index-DoDoIY_K.d.ts +79 -0
  16. package/dist/index.css +70 -63
  17. package/dist/index.css.map +1 -1
  18. package/dist/index.d.mts +1 -3
  19. package/dist/index.d.ts +1 -3
  20. package/dist/nextjs.d.mts +11 -10
  21. package/dist/nextjs.d.ts +11 -10
  22. package/dist/nextjs.js +173 -62
  23. package/dist/nextjs.js.map +1 -1
  24. package/dist/nextjs.mjs +171 -60
  25. package/dist/nextjs.mjs.map +1 -1
  26. package/dist/react.d.mts +65 -39
  27. package/dist/react.d.ts +65 -39
  28. package/dist/react.js +212 -433
  29. package/dist/react.js.map +1 -1
  30. package/dist/react.mjs +235 -456
  31. package/dist/react.mjs.map +1 -1
  32. package/dist/server.d.mts +12 -13
  33. package/dist/server.d.ts +12 -13
  34. package/dist/server.js +186 -3
  35. package/dist/server.js.map +1 -1
  36. package/dist/server.mjs +192 -9
  37. package/dist/server.mjs.map +1 -1
  38. package/package.json +4 -4
  39. package/dist/chunk-5XL2ST72.mjs +0 -226
  40. package/dist/chunk-5XL2ST72.mjs.map +0 -1
  41. package/dist/chunk-G3P5TIO2.mjs +0 -708
  42. package/dist/chunk-G3P5TIO2.mjs.map +0 -1
  43. package/dist/chunk-RF23Q4V6.js +0 -708
  44. package/dist/chunk-RF23Q4V6.js.map +0 -1
  45. package/dist/chunk-SEKF2WZX.js +0 -226
  46. package/dist/chunk-SEKF2WZX.js.map +0 -1
  47. package/dist/index-DTimUlkB.d.ts +0 -17
  48. package/dist/index-DvjkKpkk.d.mts +0 -17
  49. package/dist/types-HdCjGldB.d.mts +0 -58
  50. package/dist/types-HdCjGldB.d.ts +0 -58
  51. package/dist/types-b4c1koXj.d.mts +0 -19
  52. package/dist/types-b4c1koXj.d.ts +0 -19
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/lucas/dev/civic/civic-auth/packages/civic-auth-client/dist/server.js"],"names":[],"mappings":"AAAA;AACE;AACA;AACA;AACA;AACA;AACA;AACF,sDAA4B;AAC5B,+BAA4B;AAC5B;AACE;AACA;AACA;AACA;AACA;AACA;AACF,+UAAC","file":"/Users/lucas/dev/civic/civic-auth/packages/civic-auth-client/dist/server.js"}
1
+ {"version":3,"sources":["/Users/lucas/dev/civic/civic-auth/packages/civic-auth-client/dist/server.js","../src/shared/storage.ts","../src/services/PKCE.ts","../src/services/AuthenticationService.ts","../src/server/ServerAuthenticationResolver.ts","../src/server/login.ts","../src/server/session.ts"],"names":["OAuth2Client"],"mappings":"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","file":"/Users/lucas/dev/civic/civic-auth/packages/civic-auth-client/dist/server.js","sourcesContent":[null,"import { SessionData, UnknownObject, User } from \"@/types.js\";\n\ntype SameSiteOption = \"strict\" | \"lax\" | \"none\";\n\nexport interface SessionStorage {\n get(): SessionData;\n getUser(): User<UnknownObject> | null;\n set(data: Partial<SessionData>): void;\n setUser(data: User<UnknownObject> | null): void;\n clear(): void;\n}\n\nexport interface AuthStorage {\n get(key: string): string | null;\n set(key: string, value: string): void;\n}\n\nexport type CookieStorageSettings = {\n httpOnly: boolean;\n secure: boolean;\n sameSite: SameSiteOption;\n expires: Date;\n path: string;\n};\n\nexport const DEFAULT_COOKIE_DURATION = 60 * 15; // 15 minutes\n\nexport abstract class CookieStorage implements AuthStorage {\n protected settings: CookieStorageSettings;\n protected constructor(settings: Partial<CookieStorageSettings> = {}) {\n this.settings = {\n httpOnly: settings.httpOnly ?? true,\n secure: settings.secure ?? true,\n // the callback request comes the auth server\n // 'lax' ensures the code_verifier cookie is sent with the request\n sameSite: settings.sameSite ?? \"lax\",\n expires:\n settings.expires ??\n new Date(Date.now() + 1000 * DEFAULT_COOKIE_DURATION),\n path: settings.path ?? \"/\",\n };\n }\n abstract get(key: string): string | null;\n abstract set(key: string, value: string): void;\n}\n","import { deriveCodeChallenge } from \"@/shared/util.js\";\nimport { generateCodeVerifier } from \"oslo/oauth2\";\nimport { AuthStorage } from \"@/shared/storage.js\";\nimport { LocalStorageAdapter } from \"@/browser/storage.js\";\nimport { PKCEConsumer, PKCEProducer } from \"@/services/types.ts\";\n\n/** A PKCE consumer that retrieves the challenge from a server endpoint */\nexport class ConfidentialClientPKCEConsumer implements PKCEConsumer {\n constructor(private pkceChallengeEndpoint: string) {}\n async getCodeChallenge(): Promise<string> {\n const response = await fetch(this.pkceChallengeEndpoint);\n const data = (await response.json()) as { challenge: string };\n return data.challenge;\n }\n}\n\n/** A PKCE Producer that can generate and store a code verifier, but is agnostic as to the storage location */\nexport class GenericPublicClientPKCEProducer implements PKCEProducer {\n constructor(private storage: AuthStorage) {}\n\n // if there is already a verifier, return it,\n // If not, create a new one and store it\n async getCodeChallenge(): Promise<string> {\n // let verifier = await this.getCodeVerifier();\n // if (!verifier) {\n const verifier = generateCodeVerifier();\n this.storage.set(\"code_verifier\", verifier);\n // }\n return deriveCodeChallenge(verifier);\n }\n // if there is already a verifier, return it,\n async getCodeVerifier(): Promise<string | null> {\n return this.storage.get(\"code_verifier\");\n }\n}\n\n/** A PKCE Producer that is expected to run on a browser, and does not need a backend */\nexport class BrowserPublicClientPKCEProducer extends GenericPublicClientPKCEProducer {\n constructor() {\n super(new LocalStorageAdapter());\n }\n}\n","// Proposals for revised versions of the SessionService AKA AuthSessionService\n\nimport {\n DisplayMode,\n Endpoints,\n OIDCTokenResponseBody,\n SessionData,\n} from \"@/types.js\";\nimport { BrowserPublicClientPKCEProducer } from \"@/services/PKCE.js\";\nimport {\n exchangeTokens,\n generateOauthLoginUrl,\n generateOauthLogoutUrl,\n getEndpointsWithOverrides,\n retrieveTokens,\n storeTokens,\n} from \"@/shared/util.js\";\nimport { generateState } from \"@/lib/oauth.js\";\nimport { OAuth2Client } from \"oslo/oauth2\";\nimport { LocalStorageAdapter } from \"@/browser/storage.js\";\nimport {\n AuthenticationInitiator,\n AuthenticationResolver,\n PKCEConsumer,\n} from \"@/services/types.js\";\n\n/**\n * An authentication initiator that works on a browser. Since this is just triggering\n * login and logout, session data is not stored here.\n * An associated AuthenticationResolver would be needed to get the session data.\n * Storage is needed for the code verifier, this is the domain of the PKCEConsumer\n * The storage used by the PKCEConsumer should be available to the AuthenticationResolver.\n *\n * Example usage:\n *\n * 1) Client-only SPA -eg a react app with no server:\n * new BrowserAuthenticationInitiator({\n * pkceConsumer: new BrowserPublicClientPKCEProducer(), // generate and retrieve the challenge client-side\n * ... other config\n * })\n *\n * 2) Client-side of a client/server app - eg a react app with a backend:\n * new BrowserAuthenticationInitiator({\n * pkceConsumer: new ConfidentialClientPKCEConsumer(\"https://myserver.com/pkce\"), // get the challenge from the server\n * ... other config\n * })\n */\nexport class BrowserAuthenticationInitiator implements AuthenticationInitiator {\n protected config: {\n clientId: string;\n redirectUrl: string;\n state: string;\n scopes: string[];\n // determines whether to trigger the login/logout in an iframe, a new browser window, or redirect the current one.\n displayMode: DisplayMode;\n oauthServer: string;\n // the endpoints to use for the login (if not obtained from the auth server\n endpointOverrides?: Partial<Endpoints>;\n // used to get the PKCE challenge\n pkceConsumer: PKCEConsumer;\n };\n\n constructor(config: typeof this.config) {\n this.config = config;\n }\n\n // Use the config (Client ID, scopes OAuth Server, Endpoints, PKCEConsumer) to generate a new login url\n // and then use the display mode to decide how to send the user there\n async signIn(): Promise<URL> {\n const url = await generateOauthLoginUrl(this.config);\n // TODO open the iframe or new tab etc\n\n return url;\n }\n\n async signOut(): Promise<URL> {\n const url = await generateOauthLogoutUrl(this.config);\n // TODO open the iframe or new tab etc\n\n return url;\n }\n}\n\n/** A general-purpose authentication initiator, that just generates urls, but lets\n * the caller decide how to use them. This is useful for server-side applications\n * that may serve this URL to their front-ends or just call them directly\n */\nexport class GenericAuthenticationInitiator implements AuthenticationInitiator {\n protected config: {\n clientId: string;\n redirectUrl: string;\n state: string;\n scopes: string[];\n oauthServer: string;\n // the endpoints to use for the login (if not obtained from the auth server\n endpointOverrides?: Partial<Endpoints>;\n // used to get the PKCE challenge\n pkceConsumer: PKCEConsumer;\n };\n\n constructor(config: typeof this.config) {\n this.config = config;\n }\n\n // Use the config (Client ID, scopes OAuth Server, Endpoints, PKCEConsumer) to generate a new login url\n // and simply return the url\n async signIn(): Promise<URL> {\n return generateOauthLoginUrl(this.config);\n }\n\n async signOut(): Promise<URL> {\n return generateOauthLogoutUrl(this.config);\n }\n}\n\ntype BrowserAuthenticationConfig = {\n clientId: string;\n redirectUrl: string;\n scopes: string[];\n oauthServer: string;\n endpointOverrides?: Partial<Endpoints>;\n displayMode: DisplayMode;\n};\n\n/**\n * An authentication resolver that can run on the browser (i.e. a public client)\n * It uses PKCE for security. PKCE and Session data are stored in local storage\n */\nexport class BrowserAuthenticationService extends BrowserAuthenticationInitiator {\n private oauth2client: OAuth2Client | undefined;\n private endpoints: Endpoints | undefined;\n\n // TODO WIP - perhaps we want to keep resolver and initiator separate here\n constructor(\n config: BrowserAuthenticationConfig,\n // Since we are running fully on the client, we produce as well as consume the PKCE challenge\n protected pkceProducer = new BrowserPublicClientPKCEProducer(),\n ) {\n super({\n ...config,\n state: generateState(config.displayMode),\n // Store and retrieve the PKCE challenge in local storage\n pkceConsumer: pkceProducer,\n });\n }\n\n // TODO too much code duplication here between the browser and the server variant.\n // Suggestion for refactor: Standardise the config for AuthenticationResolvers and create a one-shot\n // function for generating an oauth2client from it\n async init(): Promise<this> {\n // resolve oauth config\n this.endpoints = await getEndpointsWithOverrides(\n this.config.oauthServer,\n this.config.endpointOverrides,\n );\n this.oauth2client = new OAuth2Client(\n this.config.clientId,\n this.endpoints.auth,\n this.endpoints.token,\n {\n redirectURI: this.config.redirectUrl,\n },\n );\n\n return this;\n }\n\n // Two responsibilities:\n // 1. resolve the auth code to get the tokens (should use library code)\n // 2. store the tokens in local storage\n async tokenExchange(\n code: string,\n state: string,\n ): Promise<OIDCTokenResponseBody> {\n if (!this.oauth2client) await this.init();\n const codeVerifier = await this.pkceProducer.getCodeVerifier();\n if (!codeVerifier) throw new Error(\"Code verifier not found in storage\");\n\n // exchange auth code for tokens\n const tokens = await exchangeTokens(\n code,\n state,\n this.pkceProducer,\n this.oauth2client!, // clean up types here to avoid the ! operator\n this.config.oauthServer,\n this.endpoints!, // clean up types here to avoid the ! operator\n );\n\n storeTokens(new LocalStorageAdapter(), tokens);\n\n return tokens;\n }\n // Get the session data from local storage\n async getSessionData(): Promise<SessionData | null> {\n const storageData = retrieveTokens(new LocalStorageAdapter());\n\n if (!storageData) return null;\n\n return {\n authenticated: !!storageData.id_token,\n idToken: storageData.id_token,\n accessToken: storageData.access_token,\n refreshToken: storageData.refresh_token,\n };\n }\n\n static async build(\n config: BrowserAuthenticationConfig,\n ): Promise<AuthenticationResolver> {\n const resolver = new BrowserAuthenticationService(config);\n await resolver.init();\n\n return resolver;\n }\n}\n","import { GenericPublicClientPKCEProducer } from \"@/services/PKCE.js\";\nimport { OAuth2Client } from \"oslo/oauth2\";\nimport { Endpoints, OIDCTokenResponseBody, SessionData } from \"@/types.js\";\nimport { AuthConfig } from \"@/server/config.js\";\nimport { AuthStorage } from \"@/shared/storage.js\";\nimport {\n exchangeTokens,\n getEndpointsWithOverrides,\n retrieveTokens,\n storeTokens,\n} from \"@/shared/util.js\";\nimport { AuthenticationResolver, PKCEProducer } from \"@/services/types.ts\";\n\nexport class ServerAuthenticationResolver implements AuthenticationResolver {\n private pkceProducer: PKCEProducer;\n private oauth2client: OAuth2Client | undefined;\n private endpoints: Endpoints | undefined;\n\n private constructor(\n readonly authConfig: AuthConfig,\n readonly storage: AuthStorage,\n readonly endpointOverrides?: Partial<Endpoints>,\n ) {\n this.pkceProducer = new GenericPublicClientPKCEProducer(storage);\n }\n\n async init(): Promise<this> {\n // resolve oauth config\n this.endpoints = await getEndpointsWithOverrides(\n this.authConfig.oauthServer,\n this.endpointOverrides,\n );\n this.oauth2client = new OAuth2Client(\n this.authConfig.clientId,\n this.endpoints.auth,\n this.endpoints.token,\n {\n redirectURI: this.authConfig.redirectUrl,\n },\n );\n\n return this;\n }\n\n async tokenExchange(\n code: string,\n state: string,\n ): Promise<OIDCTokenResponseBody> {\n if (!this.oauth2client) await this.init();\n const codeVerifier = await this.pkceProducer.getCodeVerifier();\n if (!codeVerifier) throw new Error(\"Code verifier not found in storage\");\n\n // exchange auth code for tokens\n const tokens = await exchangeTokens(\n code,\n state,\n this.pkceProducer,\n this.oauth2client!, // clean up types here to avoid the ! operator\n this.authConfig.oauthServer,\n this.endpoints!, // clean up types here to avoid the ! operator\n );\n\n storeTokens(this.storage, tokens);\n\n return tokens;\n }\n\n async getSessionData(): Promise<SessionData | null> {\n const storageData = retrieveTokens(this.storage);\n\n if (!storageData) return null;\n\n return {\n authenticated: !!storageData.id_token,\n idToken: storageData.id_token,\n accessToken: storageData.access_token,\n refreshToken: storageData.refresh_token,\n };\n }\n\n static async build(\n authConfig: AuthConfig,\n storage: AuthStorage,\n endpointOverrides?: Partial<Endpoints>,\n ): Promise<AuthenticationResolver> {\n const resolver = new ServerAuthenticationResolver(\n authConfig,\n storage,\n endpointOverrides,\n );\n await resolver.init();\n\n return resolver;\n }\n}\n","import { AuthStorage } from \"@/shared/storage.js\";\nimport { Endpoints, OIDCTokenResponseBody } from \"@/types.js\";\nimport { AUTH_SERVER, DEFAULT_SCOPES } from \"@/constants.js\";\nimport { GenericAuthenticationInitiator } from \"@/services/AuthenticationService.js\";\nimport { GenericPublicClientPKCEProducer } from \"@/services/PKCE.js\";\nimport { ServerAuthenticationResolver } from \"@/server/ServerAuthenticationResolver.js\";\n\ntype Config = {\n clientId: string;\n redirectUrl: string;\n challengeUrl: string;\n oauthServer?: string;\n endpointOverrides?: Partial<Endpoints> | undefined;\n};\n\n/**\n * Resolve an OAuth access code to a set of OIDC tokens\n * @param code The access code, typically from a query parameter in the redirect url\n * @param state The oauth random state string, used to distinguish between requests. Typically also passed in the redirect url\n * @param storage The place that this server uses to store session data (e.g. a cookie store)\n * @param config Oauth Server configuration\n */\nexport async function resolveOAuthAccessCode(\n code: string,\n state: string,\n storage: AuthStorage,\n config: Config,\n): Promise<OIDCTokenResponseBody> {\n const authSessionService = await ServerAuthenticationResolver.build(\n {\n ...config,\n oauthServer: config.oauthServer ?? AUTH_SERVER,\n },\n storage,\n config.endpointOverrides,\n );\n\n return authSessionService.tokenExchange(code, state);\n}\n\nexport function isLoggedIn(storage: AuthStorage): boolean {\n return !!storage.get(\"id_token\");\n}\n\nexport async function buildLoginUrl(\n config: Omit<Config, \"challengeUrl\"> & {\n scopes?: string[];\n state?: string;\n nonce?: string;\n },\n storage: AuthStorage,\n): Promise<URL> {\n // generate a random state if not provided\n const state = config.state ?? Math.random().toString(36).substring(2);\n const scopes = config.scopes ?? DEFAULT_SCOPES;\n const pkceProducer = new GenericPublicClientPKCEProducer(storage);\n const authInitiator = new GenericAuthenticationInitiator({\n ...config,\n state,\n scopes,\n oauthServer: config.oauthServer ?? AUTH_SERVER,\n // When retrieving the PKCE challenge on the server-side, we produce it and store it in the session\n pkceConsumer: pkceProducer,\n });\n\n return authInitiator.signIn();\n}\n","import { retrieveTokens } from \"@/shared/util.js\";\nimport { parseJWT } from \"oslo/jwt\";\nimport { User } from \"@/types.js\";\nimport { AuthStorage } from \"@/shared/storage.js\";\n\nexport async function getUser(storage: AuthStorage): Promise<User | null> {\n const tokens = retrieveTokens(storage);\n if (!tokens) return null;\n\n // Assumes all information is in the ID token\n return (parseJWT(tokens.id_token)?.payload as User) ?? null;\n}\n"]}
package/dist/server.mjs CHANGED
@@ -1,18 +1,201 @@
1
1
  import {
2
- CookieStorage,
3
- buildLoginUrl,
4
- getUser,
5
- isLoggedIn,
6
- refreshTokens,
7
- resolveOAuthAccessCode
8
- } from "./chunk-G3P5TIO2.mjs";
9
- import "./chunk-RGHW4PYM.mjs";
2
+ AUTH_SERVER,
3
+ DEFAULT_SCOPES
4
+ } from "./chunk-5NUJ7LFF.mjs";
5
+ import {
6
+ deriveCodeChallenge,
7
+ exchangeTokens,
8
+ generateOauthLoginUrl,
9
+ generateOauthLogoutUrl,
10
+ getEndpointsWithOverrides,
11
+ retrieveTokens,
12
+ storeTokens
13
+ } from "./chunk-T7HUHQ3J.mjs";
14
+ import {
15
+ __async,
16
+ __spreadProps,
17
+ __spreadValues
18
+ } from "./chunk-RGHW4PYM.mjs";
19
+
20
+ // src/shared/storage.ts
21
+ var DEFAULT_COOKIE_DURATION = 60 * 15;
22
+ var CookieStorage = class {
23
+ constructor(settings = {}) {
24
+ var _a, _b, _c, _d, _e;
25
+ this.settings = {
26
+ httpOnly: (_a = settings.httpOnly) != null ? _a : true,
27
+ secure: (_b = settings.secure) != null ? _b : true,
28
+ // the callback request comes the auth server
29
+ // 'lax' ensures the code_verifier cookie is sent with the request
30
+ sameSite: (_c = settings.sameSite) != null ? _c : "lax",
31
+ expires: (_d = settings.expires) != null ? _d : new Date(Date.now() + 1e3 * DEFAULT_COOKIE_DURATION),
32
+ path: (_e = settings.path) != null ? _e : "/"
33
+ };
34
+ }
35
+ };
36
+
37
+ // src/services/PKCE.ts
38
+ import { generateCodeVerifier } from "oslo/oauth2";
39
+ var GenericPublicClientPKCEProducer = class {
40
+ constructor(storage) {
41
+ this.storage = storage;
42
+ }
43
+ // if there is already a verifier, return it,
44
+ // If not, create a new one and store it
45
+ getCodeChallenge() {
46
+ return __async(this, null, function* () {
47
+ const verifier = generateCodeVerifier();
48
+ this.storage.set("code_verifier", verifier);
49
+ return deriveCodeChallenge(verifier);
50
+ });
51
+ }
52
+ // if there is already a verifier, return it,
53
+ getCodeVerifier() {
54
+ return __async(this, null, function* () {
55
+ return this.storage.get("code_verifier");
56
+ });
57
+ }
58
+ };
59
+
60
+ // src/services/AuthenticationService.ts
61
+ import { OAuth2Client } from "oslo/oauth2";
62
+ var GenericAuthenticationInitiator = class {
63
+ constructor(config) {
64
+ this.config = config;
65
+ }
66
+ // Use the config (Client ID, scopes OAuth Server, Endpoints, PKCEConsumer) to generate a new login url
67
+ // and simply return the url
68
+ signIn() {
69
+ return __async(this, null, function* () {
70
+ return generateOauthLoginUrl(this.config);
71
+ });
72
+ }
73
+ signOut() {
74
+ return __async(this, null, function* () {
75
+ return generateOauthLogoutUrl(this.config);
76
+ });
77
+ }
78
+ };
79
+
80
+ // src/server/ServerAuthenticationResolver.ts
81
+ import { OAuth2Client as OAuth2Client2 } from "oslo/oauth2";
82
+ var ServerAuthenticationResolver = class _ServerAuthenticationResolver {
83
+ constructor(authConfig, storage, endpointOverrides) {
84
+ this.authConfig = authConfig;
85
+ this.storage = storage;
86
+ this.endpointOverrides = endpointOverrides;
87
+ this.pkceProducer = new GenericPublicClientPKCEProducer(storage);
88
+ }
89
+ init() {
90
+ return __async(this, null, function* () {
91
+ this.endpoints = yield getEndpointsWithOverrides(
92
+ this.authConfig.oauthServer,
93
+ this.endpointOverrides
94
+ );
95
+ this.oauth2client = new OAuth2Client2(
96
+ this.authConfig.clientId,
97
+ this.endpoints.auth,
98
+ this.endpoints.token,
99
+ {
100
+ redirectURI: this.authConfig.redirectUrl
101
+ }
102
+ );
103
+ return this;
104
+ });
105
+ }
106
+ tokenExchange(code, state) {
107
+ return __async(this, null, function* () {
108
+ if (!this.oauth2client) yield this.init();
109
+ const codeVerifier = yield this.pkceProducer.getCodeVerifier();
110
+ if (!codeVerifier) throw new Error("Code verifier not found in storage");
111
+ const tokens = yield exchangeTokens(
112
+ code,
113
+ state,
114
+ this.pkceProducer,
115
+ this.oauth2client,
116
+ // clean up types here to avoid the ! operator
117
+ this.authConfig.oauthServer,
118
+ this.endpoints
119
+ // clean up types here to avoid the ! operator
120
+ );
121
+ storeTokens(this.storage, tokens);
122
+ return tokens;
123
+ });
124
+ }
125
+ getSessionData() {
126
+ return __async(this, null, function* () {
127
+ const storageData = retrieveTokens(this.storage);
128
+ if (!storageData) return null;
129
+ return {
130
+ authenticated: !!storageData.id_token,
131
+ idToken: storageData.id_token,
132
+ accessToken: storageData.access_token,
133
+ refreshToken: storageData.refresh_token
134
+ };
135
+ });
136
+ }
137
+ static build(authConfig, storage, endpointOverrides) {
138
+ return __async(this, null, function* () {
139
+ const resolver = new _ServerAuthenticationResolver(
140
+ authConfig,
141
+ storage,
142
+ endpointOverrides
143
+ );
144
+ yield resolver.init();
145
+ return resolver;
146
+ });
147
+ }
148
+ };
149
+
150
+ // src/server/login.ts
151
+ function resolveOAuthAccessCode(code, state, storage, config) {
152
+ return __async(this, null, function* () {
153
+ var _a;
154
+ const authSessionService = yield ServerAuthenticationResolver.build(
155
+ __spreadProps(__spreadValues({}, config), {
156
+ oauthServer: (_a = config.oauthServer) != null ? _a : AUTH_SERVER
157
+ }),
158
+ storage,
159
+ config.endpointOverrides
160
+ );
161
+ return authSessionService.tokenExchange(code, state);
162
+ });
163
+ }
164
+ function isLoggedIn(storage) {
165
+ return !!storage.get("id_token");
166
+ }
167
+ function buildLoginUrl(config, storage) {
168
+ return __async(this, null, function* () {
169
+ var _a, _b, _c;
170
+ const state = (_a = config.state) != null ? _a : Math.random().toString(36).substring(2);
171
+ const scopes = (_b = config.scopes) != null ? _b : DEFAULT_SCOPES;
172
+ const pkceProducer = new GenericPublicClientPKCEProducer(storage);
173
+ const authInitiator = new GenericAuthenticationInitiator(__spreadProps(__spreadValues({}, config), {
174
+ state,
175
+ scopes,
176
+ oauthServer: (_c = config.oauthServer) != null ? _c : AUTH_SERVER,
177
+ // When retrieving the PKCE challenge on the server-side, we produce it and store it in the session
178
+ pkceConsumer: pkceProducer
179
+ }));
180
+ return authInitiator.signIn();
181
+ });
182
+ }
183
+
184
+ // src/server/session.ts
185
+ import { parseJWT } from "oslo/jwt";
186
+ function getUser(storage) {
187
+ return __async(this, null, function* () {
188
+ var _a, _b;
189
+ const tokens = retrieveTokens(storage);
190
+ if (!tokens) return null;
191
+ return (_b = (_a = parseJWT(tokens.id_token)) == null ? void 0 : _a.payload) != null ? _b : null;
192
+ });
193
+ }
10
194
  export {
11
195
  CookieStorage,
12
196
  buildLoginUrl,
13
197
  getUser,
14
198
  isLoggedIn,
15
- refreshTokens,
16
199
  resolveOAuthAccessCode
17
200
  };
18
201
  //# sourceMappingURL=server.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
1
+ {"version":3,"sources":["../src/shared/storage.ts","../src/services/PKCE.ts","../src/services/AuthenticationService.ts","../src/server/ServerAuthenticationResolver.ts","../src/server/login.ts","../src/server/session.ts"],"sourcesContent":["import { SessionData, UnknownObject, User } from \"@/types.js\";\n\ntype SameSiteOption = \"strict\" | \"lax\" | \"none\";\n\nexport interface SessionStorage {\n get(): SessionData;\n getUser(): User<UnknownObject> | null;\n set(data: Partial<SessionData>): void;\n setUser(data: User<UnknownObject> | null): void;\n clear(): void;\n}\n\nexport interface AuthStorage {\n get(key: string): string | null;\n set(key: string, value: string): void;\n}\n\nexport type CookieStorageSettings = {\n httpOnly: boolean;\n secure: boolean;\n sameSite: SameSiteOption;\n expires: Date;\n path: string;\n};\n\nexport const DEFAULT_COOKIE_DURATION = 60 * 15; // 15 minutes\n\nexport abstract class CookieStorage implements AuthStorage {\n protected settings: CookieStorageSettings;\n protected constructor(settings: Partial<CookieStorageSettings> = {}) {\n this.settings = {\n httpOnly: settings.httpOnly ?? true,\n secure: settings.secure ?? true,\n // the callback request comes the auth server\n // 'lax' ensures the code_verifier cookie is sent with the request\n sameSite: settings.sameSite ?? \"lax\",\n expires:\n settings.expires ??\n new Date(Date.now() + 1000 * DEFAULT_COOKIE_DURATION),\n path: settings.path ?? \"/\",\n };\n }\n abstract get(key: string): string | null;\n abstract set(key: string, value: string): void;\n}\n","import { deriveCodeChallenge } from \"@/shared/util.js\";\nimport { generateCodeVerifier } from \"oslo/oauth2\";\nimport { AuthStorage } from \"@/shared/storage.js\";\nimport { LocalStorageAdapter } from \"@/browser/storage.js\";\nimport { PKCEConsumer, PKCEProducer } from \"@/services/types.ts\";\n\n/** A PKCE consumer that retrieves the challenge from a server endpoint */\nexport class ConfidentialClientPKCEConsumer implements PKCEConsumer {\n constructor(private pkceChallengeEndpoint: string) {}\n async getCodeChallenge(): Promise<string> {\n const response = await fetch(this.pkceChallengeEndpoint);\n const data = (await response.json()) as { challenge: string };\n return data.challenge;\n }\n}\n\n/** A PKCE Producer that can generate and store a code verifier, but is agnostic as to the storage location */\nexport class GenericPublicClientPKCEProducer implements PKCEProducer {\n constructor(private storage: AuthStorage) {}\n\n // if there is already a verifier, return it,\n // If not, create a new one and store it\n async getCodeChallenge(): Promise<string> {\n // let verifier = await this.getCodeVerifier();\n // if (!verifier) {\n const verifier = generateCodeVerifier();\n this.storage.set(\"code_verifier\", verifier);\n // }\n return deriveCodeChallenge(verifier);\n }\n // if there is already a verifier, return it,\n async getCodeVerifier(): Promise<string | null> {\n return this.storage.get(\"code_verifier\");\n }\n}\n\n/** A PKCE Producer that is expected to run on a browser, and does not need a backend */\nexport class BrowserPublicClientPKCEProducer extends GenericPublicClientPKCEProducer {\n constructor() {\n super(new LocalStorageAdapter());\n }\n}\n","// Proposals for revised versions of the SessionService AKA AuthSessionService\n\nimport {\n DisplayMode,\n Endpoints,\n OIDCTokenResponseBody,\n SessionData,\n} from \"@/types.js\";\nimport { BrowserPublicClientPKCEProducer } from \"@/services/PKCE.js\";\nimport {\n exchangeTokens,\n generateOauthLoginUrl,\n generateOauthLogoutUrl,\n getEndpointsWithOverrides,\n retrieveTokens,\n storeTokens,\n} from \"@/shared/util.js\";\nimport { generateState } from \"@/lib/oauth.js\";\nimport { OAuth2Client } from \"oslo/oauth2\";\nimport { LocalStorageAdapter } from \"@/browser/storage.js\";\nimport {\n AuthenticationInitiator,\n AuthenticationResolver,\n PKCEConsumer,\n} from \"@/services/types.js\";\n\n/**\n * An authentication initiator that works on a browser. Since this is just triggering\n * login and logout, session data is not stored here.\n * An associated AuthenticationResolver would be needed to get the session data.\n * Storage is needed for the code verifier, this is the domain of the PKCEConsumer\n * The storage used by the PKCEConsumer should be available to the AuthenticationResolver.\n *\n * Example usage:\n *\n * 1) Client-only SPA -eg a react app with no server:\n * new BrowserAuthenticationInitiator({\n * pkceConsumer: new BrowserPublicClientPKCEProducer(), // generate and retrieve the challenge client-side\n * ... other config\n * })\n *\n * 2) Client-side of a client/server app - eg a react app with a backend:\n * new BrowserAuthenticationInitiator({\n * pkceConsumer: new ConfidentialClientPKCEConsumer(\"https://myserver.com/pkce\"), // get the challenge from the server\n * ... other config\n * })\n */\nexport class BrowserAuthenticationInitiator implements AuthenticationInitiator {\n protected config: {\n clientId: string;\n redirectUrl: string;\n state: string;\n scopes: string[];\n // determines whether to trigger the login/logout in an iframe, a new browser window, or redirect the current one.\n displayMode: DisplayMode;\n oauthServer: string;\n // the endpoints to use for the login (if not obtained from the auth server\n endpointOverrides?: Partial<Endpoints>;\n // used to get the PKCE challenge\n pkceConsumer: PKCEConsumer;\n };\n\n constructor(config: typeof this.config) {\n this.config = config;\n }\n\n // Use the config (Client ID, scopes OAuth Server, Endpoints, PKCEConsumer) to generate a new login url\n // and then use the display mode to decide how to send the user there\n async signIn(): Promise<URL> {\n const url = await generateOauthLoginUrl(this.config);\n // TODO open the iframe or new tab etc\n\n return url;\n }\n\n async signOut(): Promise<URL> {\n const url = await generateOauthLogoutUrl(this.config);\n // TODO open the iframe or new tab etc\n\n return url;\n }\n}\n\n/** A general-purpose authentication initiator, that just generates urls, but lets\n * the caller decide how to use them. This is useful for server-side applications\n * that may serve this URL to their front-ends or just call them directly\n */\nexport class GenericAuthenticationInitiator implements AuthenticationInitiator {\n protected config: {\n clientId: string;\n redirectUrl: string;\n state: string;\n scopes: string[];\n oauthServer: string;\n // the endpoints to use for the login (if not obtained from the auth server\n endpointOverrides?: Partial<Endpoints>;\n // used to get the PKCE challenge\n pkceConsumer: PKCEConsumer;\n };\n\n constructor(config: typeof this.config) {\n this.config = config;\n }\n\n // Use the config (Client ID, scopes OAuth Server, Endpoints, PKCEConsumer) to generate a new login url\n // and simply return the url\n async signIn(): Promise<URL> {\n return generateOauthLoginUrl(this.config);\n }\n\n async signOut(): Promise<URL> {\n return generateOauthLogoutUrl(this.config);\n }\n}\n\ntype BrowserAuthenticationConfig = {\n clientId: string;\n redirectUrl: string;\n scopes: string[];\n oauthServer: string;\n endpointOverrides?: Partial<Endpoints>;\n displayMode: DisplayMode;\n};\n\n/**\n * An authentication resolver that can run on the browser (i.e. a public client)\n * It uses PKCE for security. PKCE and Session data are stored in local storage\n */\nexport class BrowserAuthenticationService extends BrowserAuthenticationInitiator {\n private oauth2client: OAuth2Client | undefined;\n private endpoints: Endpoints | undefined;\n\n // TODO WIP - perhaps we want to keep resolver and initiator separate here\n constructor(\n config: BrowserAuthenticationConfig,\n // Since we are running fully on the client, we produce as well as consume the PKCE challenge\n protected pkceProducer = new BrowserPublicClientPKCEProducer(),\n ) {\n super({\n ...config,\n state: generateState(config.displayMode),\n // Store and retrieve the PKCE challenge in local storage\n pkceConsumer: pkceProducer,\n });\n }\n\n // TODO too much code duplication here between the browser and the server variant.\n // Suggestion for refactor: Standardise the config for AuthenticationResolvers and create a one-shot\n // function for generating an oauth2client from it\n async init(): Promise<this> {\n // resolve oauth config\n this.endpoints = await getEndpointsWithOverrides(\n this.config.oauthServer,\n this.config.endpointOverrides,\n );\n this.oauth2client = new OAuth2Client(\n this.config.clientId,\n this.endpoints.auth,\n this.endpoints.token,\n {\n redirectURI: this.config.redirectUrl,\n },\n );\n\n return this;\n }\n\n // Two responsibilities:\n // 1. resolve the auth code to get the tokens (should use library code)\n // 2. store the tokens in local storage\n async tokenExchange(\n code: string,\n state: string,\n ): Promise<OIDCTokenResponseBody> {\n if (!this.oauth2client) await this.init();\n const codeVerifier = await this.pkceProducer.getCodeVerifier();\n if (!codeVerifier) throw new Error(\"Code verifier not found in storage\");\n\n // exchange auth code for tokens\n const tokens = await exchangeTokens(\n code,\n state,\n this.pkceProducer,\n this.oauth2client!, // clean up types here to avoid the ! operator\n this.config.oauthServer,\n this.endpoints!, // clean up types here to avoid the ! operator\n );\n\n storeTokens(new LocalStorageAdapter(), tokens);\n\n return tokens;\n }\n // Get the session data from local storage\n async getSessionData(): Promise<SessionData | null> {\n const storageData = retrieveTokens(new LocalStorageAdapter());\n\n if (!storageData) return null;\n\n return {\n authenticated: !!storageData.id_token,\n idToken: storageData.id_token,\n accessToken: storageData.access_token,\n refreshToken: storageData.refresh_token,\n };\n }\n\n static async build(\n config: BrowserAuthenticationConfig,\n ): Promise<AuthenticationResolver> {\n const resolver = new BrowserAuthenticationService(config);\n await resolver.init();\n\n return resolver;\n }\n}\n","import { GenericPublicClientPKCEProducer } from \"@/services/PKCE.js\";\nimport { OAuth2Client } from \"oslo/oauth2\";\nimport { Endpoints, OIDCTokenResponseBody, SessionData } from \"@/types.js\";\nimport { AuthConfig } from \"@/server/config.js\";\nimport { AuthStorage } from \"@/shared/storage.js\";\nimport {\n exchangeTokens,\n getEndpointsWithOverrides,\n retrieveTokens,\n storeTokens,\n} from \"@/shared/util.js\";\nimport { AuthenticationResolver, PKCEProducer } from \"@/services/types.ts\";\n\nexport class ServerAuthenticationResolver implements AuthenticationResolver {\n private pkceProducer: PKCEProducer;\n private oauth2client: OAuth2Client | undefined;\n private endpoints: Endpoints | undefined;\n\n private constructor(\n readonly authConfig: AuthConfig,\n readonly storage: AuthStorage,\n readonly endpointOverrides?: Partial<Endpoints>,\n ) {\n this.pkceProducer = new GenericPublicClientPKCEProducer(storage);\n }\n\n async init(): Promise<this> {\n // resolve oauth config\n this.endpoints = await getEndpointsWithOverrides(\n this.authConfig.oauthServer,\n this.endpointOverrides,\n );\n this.oauth2client = new OAuth2Client(\n this.authConfig.clientId,\n this.endpoints.auth,\n this.endpoints.token,\n {\n redirectURI: this.authConfig.redirectUrl,\n },\n );\n\n return this;\n }\n\n async tokenExchange(\n code: string,\n state: string,\n ): Promise<OIDCTokenResponseBody> {\n if (!this.oauth2client) await this.init();\n const codeVerifier = await this.pkceProducer.getCodeVerifier();\n if (!codeVerifier) throw new Error(\"Code verifier not found in storage\");\n\n // exchange auth code for tokens\n const tokens = await exchangeTokens(\n code,\n state,\n this.pkceProducer,\n this.oauth2client!, // clean up types here to avoid the ! operator\n this.authConfig.oauthServer,\n this.endpoints!, // clean up types here to avoid the ! operator\n );\n\n storeTokens(this.storage, tokens);\n\n return tokens;\n }\n\n async getSessionData(): Promise<SessionData | null> {\n const storageData = retrieveTokens(this.storage);\n\n if (!storageData) return null;\n\n return {\n authenticated: !!storageData.id_token,\n idToken: storageData.id_token,\n accessToken: storageData.access_token,\n refreshToken: storageData.refresh_token,\n };\n }\n\n static async build(\n authConfig: AuthConfig,\n storage: AuthStorage,\n endpointOverrides?: Partial<Endpoints>,\n ): Promise<AuthenticationResolver> {\n const resolver = new ServerAuthenticationResolver(\n authConfig,\n storage,\n endpointOverrides,\n );\n await resolver.init();\n\n return resolver;\n }\n}\n","import { AuthStorage } from \"@/shared/storage.js\";\nimport { Endpoints, OIDCTokenResponseBody } from \"@/types.js\";\nimport { AUTH_SERVER, DEFAULT_SCOPES } from \"@/constants.js\";\nimport { GenericAuthenticationInitiator } from \"@/services/AuthenticationService.js\";\nimport { GenericPublicClientPKCEProducer } from \"@/services/PKCE.js\";\nimport { ServerAuthenticationResolver } from \"@/server/ServerAuthenticationResolver.js\";\n\ntype Config = {\n clientId: string;\n redirectUrl: string;\n challengeUrl: string;\n oauthServer?: string;\n endpointOverrides?: Partial<Endpoints> | undefined;\n};\n\n/**\n * Resolve an OAuth access code to a set of OIDC tokens\n * @param code The access code, typically from a query parameter in the redirect url\n * @param state The oauth random state string, used to distinguish between requests. Typically also passed in the redirect url\n * @param storage The place that this server uses to store session data (e.g. a cookie store)\n * @param config Oauth Server configuration\n */\nexport async function resolveOAuthAccessCode(\n code: string,\n state: string,\n storage: AuthStorage,\n config: Config,\n): Promise<OIDCTokenResponseBody> {\n const authSessionService = await ServerAuthenticationResolver.build(\n {\n ...config,\n oauthServer: config.oauthServer ?? AUTH_SERVER,\n },\n storage,\n config.endpointOverrides,\n );\n\n return authSessionService.tokenExchange(code, state);\n}\n\nexport function isLoggedIn(storage: AuthStorage): boolean {\n return !!storage.get(\"id_token\");\n}\n\nexport async function buildLoginUrl(\n config: Omit<Config, \"challengeUrl\"> & {\n scopes?: string[];\n state?: string;\n nonce?: string;\n },\n storage: AuthStorage,\n): Promise<URL> {\n // generate a random state if not provided\n const state = config.state ?? Math.random().toString(36).substring(2);\n const scopes = config.scopes ?? DEFAULT_SCOPES;\n const pkceProducer = new GenericPublicClientPKCEProducer(storage);\n const authInitiator = new GenericAuthenticationInitiator({\n ...config,\n state,\n scopes,\n oauthServer: config.oauthServer ?? AUTH_SERVER,\n // When retrieving the PKCE challenge on the server-side, we produce it and store it in the session\n pkceConsumer: pkceProducer,\n });\n\n return authInitiator.signIn();\n}\n","import { retrieveTokens } from \"@/shared/util.js\";\nimport { parseJWT } from \"oslo/jwt\";\nimport { User } from \"@/types.js\";\nimport { AuthStorage } from \"@/shared/storage.js\";\n\nexport async function getUser(storage: AuthStorage): Promise<User | null> {\n const tokens = retrieveTokens(storage);\n if (!tokens) return null;\n\n // Assumes all information is in the ID token\n return (parseJWT(tokens.id_token)?.payload as User) ?? null;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAyBO,IAAM,0BAA0B,KAAK;AAErC,IAAe,gBAAf,MAAoD;AAAA,EAE/C,YAAY,WAA2C,CAAC,GAAG;AA7BvE;AA8BI,SAAK,WAAW;AAAA,MACd,WAAU,cAAS,aAAT,YAAqB;AAAA,MAC/B,SAAQ,cAAS,WAAT,YAAmB;AAAA;AAAA;AAAA,MAG3B,WAAU,cAAS,aAAT,YAAqB;AAAA,MAC/B,UACE,cAAS,YAAT,YACA,IAAI,KAAK,KAAK,IAAI,IAAI,MAAO,uBAAuB;AAAA,MACtD,OAAM,cAAS,SAAT,YAAiB;AAAA,IACzB;AAAA,EACF;AAGF;;;AC3CA,SAAS,4BAA4B;AAgB9B,IAAM,kCAAN,MAA8D;AAAA,EACnE,YAAoB,SAAsB;AAAtB;AAAA,EAAuB;AAAA;AAAA;AAAA,EAIrC,mBAAoC;AAAA;AAGxC,YAAM,WAAW,qBAAqB;AACtC,WAAK,QAAQ,IAAI,iBAAiB,QAAQ;AAE1C,aAAO,oBAAoB,QAAQ;AAAA,IACrC;AAAA;AAAA;AAAA,EAEM,kBAA0C;AAAA;AAC9C,aAAO,KAAK,QAAQ,IAAI,eAAe;AAAA,IACzC;AAAA;AACF;;;AChBA,SAAS,oBAAoB;AAqEtB,IAAM,iCAAN,MAAwE;AAAA,EAa7E,YAAY,QAA4B;AACtC,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA;AAAA,EAIM,SAAuB;AAAA;AAC3B,aAAO,sBAAsB,KAAK,MAAM;AAAA,IAC1C;AAAA;AAAA,EAEM,UAAwB;AAAA;AAC5B,aAAO,uBAAuB,KAAK,MAAM;AAAA,IAC3C;AAAA;AACF;;;AChHA,SAAS,gBAAAA,qBAAoB;AAYtB,IAAM,+BAAN,MAAM,8BAA+D;AAAA,EAKlE,YACG,YACA,SACA,mBACT;AAHS;AACA;AACA;AAET,SAAK,eAAe,IAAI,gCAAgC,OAAO;AAAA,EACjE;AAAA,EAEM,OAAsB;AAAA;AAE1B,WAAK,YAAY,MAAM;AAAA,QACrB,KAAK,WAAW;AAAA,QAChB,KAAK;AAAA,MACP;AACA,WAAK,eAAe,IAAIC;AAAA,QACtB,KAAK,WAAW;AAAA,QAChB,KAAK,UAAU;AAAA,QACf,KAAK,UAAU;AAAA,QACf;AAAA,UACE,aAAa,KAAK,WAAW;AAAA,QAC/B;AAAA,MACF;AAEA,aAAO;AAAA,IACT;AAAA;AAAA,EAEM,cACJ,MACA,OACgC;AAAA;AAChC,UAAI,CAAC,KAAK,aAAc,OAAM,KAAK,KAAK;AACxC,YAAM,eAAe,MAAM,KAAK,aAAa,gBAAgB;AAC7D,UAAI,CAAC,aAAc,OAAM,IAAI,MAAM,oCAAoC;AAGvE,YAAM,SAAS,MAAM;AAAA,QACnB;AAAA,QACA;AAAA,QACA,KAAK;AAAA,QACL,KAAK;AAAA;AAAA,QACL,KAAK,WAAW;AAAA,QAChB,KAAK;AAAA;AAAA,MACP;AAEA,kBAAY,KAAK,SAAS,MAAM;AAEhC,aAAO;AAAA,IACT;AAAA;AAAA,EAEM,iBAA8C;AAAA;AAClD,YAAM,cAAc,eAAe,KAAK,OAAO;AAE/C,UAAI,CAAC,YAAa,QAAO;AAEzB,aAAO;AAAA,QACL,eAAe,CAAC,CAAC,YAAY;AAAA,QAC7B,SAAS,YAAY;AAAA,QACrB,aAAa,YAAY;AAAA,QACzB,cAAc,YAAY;AAAA,MAC5B;AAAA,IACF;AAAA;AAAA,EAEA,OAAa,MACX,YACA,SACA,mBACiC;AAAA;AACjC,YAAM,WAAW,IAAI;AAAA,QACnB;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA,YAAM,SAAS,KAAK;AAEpB,aAAO;AAAA,IACT;AAAA;AACF;;;ACxEA,SAAsB,uBACpB,MACA,OACA,SACA,QACgC;AAAA;AA3BlC;AA4BE,UAAM,qBAAqB,MAAM,6BAA6B;AAAA,MAC5D,iCACK,SADL;AAAA,QAEE,cAAa,YAAO,gBAAP,YAAsB;AAAA,MACrC;AAAA,MACA;AAAA,MACA,OAAO;AAAA,IACT;AAEA,WAAO,mBAAmB,cAAc,MAAM,KAAK;AAAA,EACrD;AAAA;AAEO,SAAS,WAAW,SAA+B;AACxD,SAAO,CAAC,CAAC,QAAQ,IAAI,UAAU;AACjC;AAEA,SAAsB,cACpB,QAKA,SACc;AAAA;AAnDhB;AAqDE,UAAM,SAAQ,YAAO,UAAP,YAAgB,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,CAAC;AACpE,UAAM,UAAS,YAAO,WAAP,YAAiB;AAChC,UAAM,eAAe,IAAI,gCAAgC,OAAO;AAChE,UAAM,gBAAgB,IAAI,+BAA+B,iCACpD,SADoD;AAAA,MAEvD;AAAA,MACA;AAAA,MACA,cAAa,YAAO,gBAAP,YAAsB;AAAA;AAAA,MAEnC,cAAc;AAAA,IAChB,EAAC;AAED,WAAO,cAAc,OAAO;AAAA,EAC9B;AAAA;;;ACjEA,SAAS,gBAAgB;AAIzB,SAAsB,QAAQ,SAA4C;AAAA;AAL1E;AAME,UAAM,SAAS,eAAe,OAAO;AACrC,QAAI,CAAC,OAAQ,QAAO;AAGpB,YAAQ,oBAAS,OAAO,QAAQ,MAAxB,mBAA2B,YAA3B,YAA+C;AAAA,EACzD;AAAA;","names":["OAuth2Client","OAuth2Client"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@civic/auth",
3
- "version": "0.0.1-beta.18",
3
+ "version": "0.0.1-beta.2",
4
4
  "files": [
5
5
  "dist"
6
6
  ],
@@ -50,7 +50,7 @@
50
50
  "scripts": {
51
51
  "build": "tsup",
52
52
  "prepublish": "tsup",
53
- "dev": "tsup --watch",
53
+ "dev:watch": "tsup --watch",
54
54
  "test": "vitest",
55
55
  "lint": "eslint \"src/**/*.ts*\"",
56
56
  "lint:fix": "pnpm lint --fix",
@@ -83,7 +83,7 @@
83
83
  "@vitejs/plugin-react": "^4.3.2",
84
84
  "esbuild-plugin-css-modules": "^0.3.0",
85
85
  "eslint": "^8.57.1",
86
- "next": "14.2.15",
86
+ "next": "14.2.14",
87
87
  "prettier": "^3.3.3",
88
88
  "prettier-plugin-tailwindcss": "^0.6.8",
89
89
  "react": "18.3.1",
@@ -95,6 +95,6 @@
95
95
  "vitest": "^2.1.2"
96
96
  },
97
97
  "peerDependencies": {
98
- "next": "^14"
98
+ "next": "^14.2.10"
99
99
  }
100
100
  }
@@ -1,226 +0,0 @@
1
- import {
2
- CookieStorage,
3
- GenericUserSession,
4
- clearTokens,
5
- withoutUndefined
6
- } from "./chunk-G3P5TIO2.mjs";
7
- import {
8
- __async,
9
- __spreadProps,
10
- __spreadValues
11
- } from "./chunk-RGHW4PYM.mjs";
12
-
13
- // src/lib/logger.ts
14
- import debug from "debug";
15
- var PACKAGE_NAME = "@civic/auth";
16
- var DebugLogger = class {
17
- constructor(namespace) {
18
- this.debugLogger = debug(`${PACKAGE_NAME}:${namespace}:debug`);
19
- this.infoLogger = debug(`${PACKAGE_NAME}:${namespace}:info`);
20
- this.warnLogger = debug(`${PACKAGE_NAME}:${namespace}:warn`);
21
- this.errorLogger = debug(`${PACKAGE_NAME}:${namespace}:error`);
22
- this.debugLogger.color = "4";
23
- this.infoLogger.color = "2";
24
- this.warnLogger.color = "3";
25
- this.errorLogger.color = "1";
26
- }
27
- debug(message, ...args) {
28
- this.debugLogger(message, ...args);
29
- }
30
- info(message, ...args) {
31
- this.infoLogger(message, ...args);
32
- }
33
- warn(message, ...args) {
34
- this.warnLogger(message, ...args);
35
- }
36
- error(message, ...args) {
37
- this.errorLogger(message, ...args);
38
- }
39
- };
40
- var createLogger = (namespace) => new DebugLogger(namespace);
41
- var loggers = {
42
- // Next.js specific loggers
43
- nextjs: {
44
- routes: createLogger("api:routes"),
45
- middleware: createLogger("api:middleware"),
46
- handlers: {
47
- auth: createLogger("api:handlers:auth")
48
- }
49
- },
50
- // React specific loggers
51
- react: {
52
- components: createLogger("react:components"),
53
- hooks: createLogger("react:hooks"),
54
- context: createLogger("react:context")
55
- },
56
- // Shared utilities loggers
57
- services: {
58
- validation: createLogger("utils:validation"),
59
- network: createLogger("utils:network")
60
- }
61
- };
62
-
63
- // src/nextjs/config.ts
64
- var logger = loggers.nextjs.handlers.auth;
65
- var defaultAuthConfig = {
66
- oauthServer: "https://auth-dev.civic.com/oauth",
67
- callbackUrl: "/api/auth/callback",
68
- challengeUrl: "/api/auth/challenge",
69
- logoutUrl: "/api/auth/logout",
70
- loginUrl: "/",
71
- include: ["/*"],
72
- exclude: [],
73
- cookies: {
74
- tokens: {
75
- ["id_token" /* ID_TOKEN */]: {
76
- secure: true,
77
- httpOnly: true,
78
- sameSite: "strict",
79
- path: "/"
80
- },
81
- ["access_token" /* ACCESS_TOKEN */]: {
82
- secure: true,
83
- httpOnly: true,
84
- sameSite: "strict",
85
- path: "/"
86
- },
87
- ["refresh_token" /* REFRESH_TOKEN */]: {
88
- secure: true,
89
- httpOnly: true,
90
- sameSite: "strict",
91
- path: "/"
92
- }
93
- },
94
- user: {
95
- secure: true,
96
- httpOnly: false,
97
- sameSite: "strict",
98
- path: "/",
99
- maxAge: 60 * 60
100
- // 1 hour
101
- }
102
- }
103
- };
104
- var resolveAuthConfig = (config = {}) => {
105
- var _a, _b, _c, _d, _e, _f;
106
- logger.debug("resolveAuthConfig inputs", JSON.stringify(config, null, 2));
107
- const configFromEnv = withoutUndefined({
108
- clientId: process.env._civic_auth_client_id,
109
- oauthServer: process.env._civic_oauth_server,
110
- callbackUrl: process.env._civic_auth_callback_url,
111
- challengeUrl: process.env._civic_auth_challenge_url,
112
- loginUrl: process.env._civic_auth_login_url,
113
- appUrl: process.env._civic_auth_app_url,
114
- logoutUrl: process.env._civic_auth_logout_url,
115
- include: (_a = process.env._civic_auth_includes) == null ? void 0 : _a.split(","),
116
- exclude: (_b = process.env._civic_auth_excludes) == null ? void 0 : _b.split(","),
117
- cookies: process.env._civic_auth_cookie_config ? JSON.parse(process.env._civic_auth_cookie_config) : void 0
118
- });
119
- const mergedConfig = __spreadProps(__spreadValues(__spreadValues(__spreadValues({}, defaultAuthConfig), configFromEnv), config), {
120
- // Override with directly passed config
121
- cookies: {
122
- tokens: __spreadValues(__spreadValues(__spreadValues({}, defaultAuthConfig.cookies.tokens), ((_c = configFromEnv == null ? void 0 : configFromEnv.cookies) == null ? void 0 : _c.tokens) || {}), ((_d = config.cookies) == null ? void 0 : _d.tokens) || {}),
123
- user: __spreadValues(__spreadValues(__spreadValues({}, defaultAuthConfig.cookies.user), ((_e = configFromEnv == null ? void 0 : configFromEnv.cookies) == null ? void 0 : _e.user) || {}), ((_f = config.cookies) == null ? void 0 : _f.user) || {})
124
- }
125
- });
126
- logger.debug(
127
- "Config from environment:",
128
- JSON.stringify(configFromEnv, null, 2)
129
- );
130
- logger.debug("Resolved config:", JSON.stringify(mergedConfig, null, 2));
131
- if (mergedConfig.clientId === void 0) {
132
- throw new Error("Civic Auth client ID is required");
133
- }
134
- return mergedConfig;
135
- };
136
- var createCivicAuthPlugin = (clientId, authConfig = {}) => {
137
- return (nextConfig) => {
138
- logger.debug(
139
- "createCivicAuthPlugin nextConfig",
140
- JSON.stringify(nextConfig, null, 2)
141
- );
142
- const resolvedConfig = resolveAuthConfig(__spreadProps(__spreadValues({}, authConfig), { clientId }));
143
- return __spreadProps(__spreadValues({}, nextConfig), {
144
- env: __spreadProps(__spreadValues({}, nextConfig == null ? void 0 : nextConfig.env), {
145
- // Internal environment variables - do not set these manually
146
- _civic_auth_client_id: clientId,
147
- _civic_oauth_server: resolvedConfig.oauthServer,
148
- _civic_auth_callback_url: resolvedConfig.callbackUrl,
149
- _civic_auth_challenge_url: resolvedConfig.challengeUrl,
150
- _civic_auth_login_url: resolvedConfig.loginUrl,
151
- _civic_auth_logout_url: resolvedConfig.logoutUrl,
152
- _civic_auth_app_url: resolvedConfig.appUrl,
153
- _civic_auth_includes: resolvedConfig.include.join(","),
154
- _civic_auth_excludes: resolvedConfig.exclude.join(","),
155
- _civic_auth_cookie_config: JSON.stringify(resolvedConfig.cookies)
156
- })
157
- });
158
- };
159
- };
160
-
161
- // src/nextjs/utils.ts
162
- var resolveCallbackUrl = (config, alternativeUrl) => {
163
- var _a;
164
- const baseUrl = (_a = config.appUrl) != null ? _a : alternativeUrl;
165
- const callbackUrl = new URL(config == null ? void 0 : config.callbackUrl, baseUrl).toString();
166
- return callbackUrl.toString();
167
- };
168
-
169
- // src/nextjs/cookies.ts
170
- import { cookies } from "next/headers.js";
171
- var clearAuthCookies = () => __async(void 0, null, function* () {
172
- const cookieStorage = new NextjsCookieStorage();
173
- clearTokens(cookieStorage);
174
- const clientStorage = new NextjsClientStorage();
175
- const userSession = new GenericUserSession(clientStorage);
176
- userSession.set(null);
177
- });
178
- var NextjsCookieStorage = class extends CookieStorage {
179
- constructor(config = {}) {
180
- super({
181
- secure: true,
182
- httpOnly: true
183
- });
184
- this.config = config;
185
- }
186
- get(key) {
187
- var _a;
188
- return ((_a = cookies().get(key)) == null ? void 0 : _a.value) || null;
189
- }
190
- set(key, value) {
191
- var _a, _b;
192
- const cookieSettings = (_b = (_a = this.config) == null ? void 0 : _a[key]) != null ? _b : this.settings;
193
- console.log(
194
- "NextjsCookieStorage.set",
195
- JSON.stringify({ key, value, cookieSettings }, null, 2)
196
- );
197
- cookies().set(key, value, cookieSettings);
198
- }
199
- };
200
- var NextjsClientStorage = class extends CookieStorage {
201
- constructor(config = {}) {
202
- super(__spreadProps(__spreadValues({}, config), {
203
- secure: false,
204
- httpOnly: false
205
- }));
206
- }
207
- get(key) {
208
- var _a;
209
- return ((_a = cookies().get(key)) == null ? void 0 : _a.value) || null;
210
- }
211
- set(key, value) {
212
- cookies().set(key, value, this.settings);
213
- }
214
- };
215
-
216
- export {
217
- loggers,
218
- defaultAuthConfig,
219
- resolveAuthConfig,
220
- createCivicAuthPlugin,
221
- resolveCallbackUrl,
222
- clearAuthCookies,
223
- NextjsCookieStorage,
224
- NextjsClientStorage
225
- };
226
- //# sourceMappingURL=chunk-5XL2ST72.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lib/logger.ts","../src/nextjs/config.ts","../src/nextjs/utils.ts","../src/nextjs/cookies.ts"],"sourcesContent":["import debug from \"debug\";\n\nconst PACKAGE_NAME = \"@civic/auth\";\n\nexport interface Logger {\n debug(message: string, ...args: unknown[]): void;\n info(message: string, ...args: unknown[]): void;\n warn(message: string, ...args: unknown[]): void;\n error(message: string, ...args: unknown[]): void;\n}\n\nclass DebugLogger implements Logger {\n private debugLogger: debug.Debugger;\n private infoLogger: debug.Debugger;\n private warnLogger: debug.Debugger;\n private errorLogger: debug.Debugger;\n\n constructor(namespace: string) {\n // Format: @org/package:library:component:level\n this.debugLogger = debug(`${PACKAGE_NAME}:${namespace}:debug`);\n this.infoLogger = debug(`${PACKAGE_NAME}:${namespace}:info`);\n this.warnLogger = debug(`${PACKAGE_NAME}:${namespace}:warn`);\n this.errorLogger = debug(`${PACKAGE_NAME}:${namespace}:error`);\n\n this.debugLogger.color = \"4\";\n this.infoLogger.color = \"2\";\n this.warnLogger.color = \"3\";\n this.errorLogger.color = \"1\";\n }\n\n debug(message: string, ...args: unknown[]): void {\n this.debugLogger(message, ...args);\n }\n\n info(message: string, ...args: unknown[]): void {\n this.infoLogger(message, ...args);\n }\n\n warn(message: string, ...args: unknown[]): void {\n this.warnLogger(message, ...args);\n }\n\n error(message: string, ...args: unknown[]): void {\n this.errorLogger(message, ...args);\n }\n}\n\nexport const createLogger = (namespace: string): Logger =>\n new DebugLogger(namespace);\n\n// Pre-configured loggers for different parts of your package\nexport const loggers = {\n // Next.js specific loggers\n nextjs: {\n routes: createLogger(\"api:routes\"),\n middleware: createLogger(\"api:middleware\"),\n handlers: {\n auth: createLogger(\"api:handlers:auth\"),\n },\n },\n // React specific loggers\n react: {\n components: createLogger(\"react:components\"),\n hooks: createLogger(\"react:hooks\"),\n context: createLogger(\"react:context\"),\n },\n // Shared utilities loggers\n services: {\n validation: createLogger(\"utils:validation\"),\n network: createLogger(\"utils:network\"),\n },\n} as const;\n","/* eslint-disable turbo/no-undeclared-env-vars */\nimport { NextConfig } from \"next\";\nimport { loggers } from \"@/lib/logger\";\nimport { withoutUndefined } from \"@/utils\";\nimport { CookieConfig, OAuthTokens, TokensCookieConfig } from \"@/shared/types\";\n\nconst logger = loggers.nextjs.handlers.auth;\n\nexport type AuthConfigWithDefaults = {\n clientId: string;\n oauthServer: string;\n callbackUrl: string;\n loginUrl: string;\n logoutUrl: string;\n appUrl?: string;\n challengeUrl: string;\n include: string[];\n exclude: string[];\n cookies: {\n tokens: TokensCookieConfig;\n user: CookieConfig;\n };\n};\n\nexport type AuthConfig = Partial<AuthConfigWithDefaults>;\n\nexport type DefinedAuthConfig = AuthConfigWithDefaults;\n\n/**\n * Default configuration values that will be used if not overridden\n */\nexport const defaultAuthConfig: Omit<AuthConfigWithDefaults, \"clientId\"> = {\n oauthServer: \"https://auth-dev.civic.com/oauth\",\n callbackUrl: \"/api/auth/callback\",\n challengeUrl: \"/api/auth/challenge\",\n logoutUrl: \"/api/auth/logout\",\n loginUrl: \"/\",\n include: [\"/*\"],\n exclude: [],\n cookies: {\n tokens: {\n [OAuthTokens.ID_TOKEN]: {\n secure: true,\n httpOnly: true,\n sameSite: \"strict\",\n path: \"/\",\n },\n [OAuthTokens.ACCESS_TOKEN]: {\n secure: true,\n httpOnly: true,\n sameSite: \"strict\",\n path: \"/\",\n },\n [OAuthTokens.REFRESH_TOKEN]: {\n secure: true,\n httpOnly: true,\n sameSite: \"strict\",\n path: \"/\",\n },\n },\n user: {\n secure: true,\n httpOnly: false,\n sameSite: \"strict\",\n path: \"/\",\n maxAge: 60 * 60, // 1 hour\n },\n },\n};\n\n/**\n * Resolves the authentication configuration by combining:\n * 1. Default values\n * 2. Environment variables (set internally by the plugin)\n * 3. Explicitly passed configuration\n *\n * Note: Developers should not set _civic_auth_* environment variables directly.\n * Instead, pass configuration to the createCivicAuthPlugin in next.config.js:\n *\n * @example\n * ```js\n * // next.config.js\n * export default createCivicAuthPlugin({\n * callbackUrl: '/custom/callback',\n * })\n * ```\n */\nexport const resolveAuthConfig = (\n config: AuthConfig = {},\n): AuthConfigWithDefaults & { clientId: string } => {\n logger.debug(\"resolveAuthConfig inputs\", JSON.stringify(config, null, 2));\n // Read configuration that was set by the plugin via environment variables\n const configFromEnv = withoutUndefined({\n clientId: process.env._civic_auth_client_id,\n oauthServer: process.env._civic_oauth_server,\n callbackUrl: process.env._civic_auth_callback_url,\n challengeUrl: process.env._civic_auth_challenge_url,\n loginUrl: process.env._civic_auth_login_url,\n appUrl: process.env._civic_auth_app_url,\n logoutUrl: process.env._civic_auth_logout_url,\n include: process.env._civic_auth_includes?.split(\",\"),\n exclude: process.env._civic_auth_excludes?.split(\",\"),\n cookies: process.env._civic_auth_cookie_config\n ? JSON.parse(process.env._civic_auth_cookie_config)\n : undefined,\n }) as AuthConfig;\n const mergedConfig = {\n ...defaultAuthConfig,\n ...configFromEnv, // Apply plugin-set config\n ...config, // Override with directly passed config\n cookies: {\n tokens: {\n ...defaultAuthConfig.cookies.tokens,\n ...(configFromEnv?.cookies?.tokens || {}),\n ...(config.cookies?.tokens || {}),\n },\n user: {\n ...defaultAuthConfig.cookies.user,\n ...(configFromEnv?.cookies?.user || {}),\n ...(config.cookies?.user || {}),\n },\n },\n };\n\n logger.debug(\n \"Config from environment:\",\n JSON.stringify(configFromEnv, null, 2),\n );\n logger.debug(\"Resolved config:\", JSON.stringify(mergedConfig, null, 2));\n if (mergedConfig.clientId === undefined) {\n throw new Error(\"Civic Auth client ID is required\");\n }\n return mergedConfig as AuthConfigWithDefaults & { clientId: string };\n};\n\n/**\n * Creates a Next.js plugin that handles auth configuration.\n *\n * This is the main configuration point for the auth system.\n * Do not set _civic_auth_* environment variables directly - instead,\n * pass your configuration here:\n *\n * @example\n * ```js\n * // next.config.js\n * export default createCivicAuthPlugin({\n * clientId: 'my-client-id',\n * callbackUrl: '/custom/callback',\n * loginUrl: '/custom/login',\n * logoutUrl: '/custom/logout',\n * include: ['/protected/*'],\n * exclude: ['/public/*']\n * })\n * ```\n *\n * The plugin sets internal environment variables that are used by\n * the auth system. These variables should not be set manually.\n */\nexport const createCivicAuthPlugin = (\n clientId: string,\n authConfig: AuthConfig = {},\n) => {\n return (nextConfig?: NextConfig) => {\n logger.debug(\n \"createCivicAuthPlugin nextConfig\",\n JSON.stringify(nextConfig, null, 2),\n );\n const resolvedConfig = resolveAuthConfig({ ...authConfig, clientId });\n return {\n ...nextConfig,\n env: {\n ...nextConfig?.env,\n // Internal environment variables - do not set these manually\n _civic_auth_client_id: clientId,\n _civic_oauth_server: resolvedConfig.oauthServer,\n _civic_auth_callback_url: resolvedConfig.callbackUrl,\n _civic_auth_challenge_url: resolvedConfig.challengeUrl,\n _civic_auth_login_url: resolvedConfig.loginUrl,\n _civic_auth_logout_url: resolvedConfig.logoutUrl,\n _civic_auth_app_url: resolvedConfig.appUrl,\n _civic_auth_includes: resolvedConfig.include.join(\",\"),\n _civic_auth_excludes: resolvedConfig.exclude.join(\",\"),\n _civic_auth_cookie_config: JSON.stringify(resolvedConfig.cookies),\n },\n };\n };\n};\n","import { AuthConfigWithDefaults } from \"@/nextjs/config\";\n\nexport const resolveCallbackUrl = (\n config: AuthConfigWithDefaults,\n alternativeUrl?: string,\n): string => {\n const baseUrl = config.appUrl ?? alternativeUrl;\n const callbackUrl = new URL(config?.callbackUrl, baseUrl).toString();\n return callbackUrl.toString();\n};\n","import { SessionData, UnknownObject, User } from \"@/types\";\nimport { NextResponse } from \"next/server\";\nimport { AuthConfig } from \"@/nextjs/config\";\nimport { CookieStorage, CookieStorageSettings } from \"@/server\";\nimport { cookies } from \"next/headers.js\";\nimport { GenericUserSession } from \"@/shared/UserSession\";\nimport { clearTokens } from \"@/shared/util\";\nimport { OAuthTokens, TokensCookieConfig } from \"@/shared/types\";\n\n/**\n * Creates HTTP-only cookies for authentication tokens\n */\nconst createTokenCookies = (\n response: NextResponse,\n sessionData: SessionData,\n config: AuthConfig,\n) => {\n const maxAge = sessionData.expiresIn ?? 3600;\n const cookieOptions = {\n ...config.cookies?.tokens,\n maxAge,\n };\n\n if (sessionData.accessToken) {\n response.cookies.set(\"access_token\", sessionData.accessToken, {\n ...cookieOptions,\n httpOnly: true,\n });\n }\n\n if (sessionData.idToken) {\n response.cookies.set(\"id_token\", sessionData.idToken, {\n ...cookieOptions,\n httpOnly: true,\n });\n }\n\n if (sessionData.refreshToken) {\n response.cookies.set(\"refresh_token\", sessionData.refreshToken, {\n ...cookieOptions,\n httpOnly: true,\n });\n }\n};\n\n/**\n * Creates a client-readable cookie with user info\n */\nconst createUserInfoCookie = (\n response: NextResponse,\n user: User<UnknownObject> | null,\n sessionData: SessionData,\n config: AuthConfig,\n) => {\n if (!user) {\n response.cookies.set(\"user\", \"\", {\n ...config.cookies?.user,\n maxAge: 0,\n });\n return;\n }\n const maxAge = sessionData.expiresIn ?? 3600;\n\n // TODO select fields to include in the user cookie\n const frontendUser = {\n ...user,\n };\n\n // TODO make call to get user info from the\n // auth server /userinfo endpoint when it's available\n // then add to the default claims above\n\n response.cookies.set(\"user\", JSON.stringify(frontendUser), {\n ...config.cookies?.user,\n maxAge,\n });\n};\n\n/**\n * Clears all authentication cookies\n */\nconst clearAuthCookies = async () => {\n // clear session, and tokens\n const cookieStorage = new NextjsCookieStorage();\n clearTokens(cookieStorage);\n\n // clear user\n const clientStorage = new NextjsClientStorage();\n const userSession = new GenericUserSession(clientStorage);\n userSession.set(null);\n};\n\nclass NextjsCookieStorage extends CookieStorage {\n constructor(readonly config: Partial<TokensCookieConfig> = {}) {\n super({\n secure: true,\n httpOnly: true,\n });\n }\n\n get(key: string): string | null {\n return cookies().get(key)?.value || null;\n }\n\n set(key: OAuthTokens, value: string): void {\n const cookieSettings = this.config?.[key] ?? this.settings;\n console.log(\n \"NextjsCookieStorage.set\",\n JSON.stringify({ key, value, cookieSettings }, null, 2),\n );\n cookies().set(key, value, cookieSettings);\n }\n}\n\nclass NextjsClientStorage extends CookieStorage {\n constructor(config: Partial<CookieStorageSettings> = {}) {\n super({\n ...config,\n secure: false,\n httpOnly: false,\n });\n }\n\n get(key: string): string | null {\n return cookies().get(key)?.value || null;\n }\n\n set(key: string, value: string): void {\n cookies().set(key, value, this.settings);\n }\n}\n\nexport {\n createTokenCookies,\n createUserInfoCookie,\n clearAuthCookies,\n NextjsCookieStorage,\n NextjsClientStorage,\n};\n"],"mappings":"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","names":[]}