@growth-labs/auth 0.7.0 → 0.7.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.
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/package.json +7 -2
- package/src/client/login-page.ts +12 -0
- package/src/cloudflare-workers.d.ts +3 -0
- package/src/config-store.ts +70 -0
- package/src/index.ts +177 -0
- package/src/middleware/auth.ts +88 -0
- package/src/options.ts +75 -0
- package/src/renderers.ts +182 -0
- package/src/routes/api/code-request.ts +33 -0
- package/src/routes/api/code-verify.ts +35 -0
- package/src/routes/api/password-reset-confirm.ts +36 -0
- package/src/routes/api/password-reset-request.ts +32 -0
- package/src/routes/api/password-verify.ts +35 -0
- package/src/routes/api/shared.ts +130 -0
- package/src/routes/callback.ts +105 -0
- package/src/routes/flow.ts +53 -0
- package/src/routes/forgot-password.ts +31 -0
- package/src/routes/login.ts +130 -0
- package/src/routes/logout.ts +59 -0
- package/src/routes/password.ts +27 -0
- package/src/routes/reset-password.ts +33 -0
- package/src/routes/verify-code.ts +28 -0
- package/src/runtime-bridge.ts +15 -0
- package/src/runtime.ts +5 -0
- package/src/server.ts +185 -0
- package/src/types.ts +158 -0
- package/src/utils/analytics.ts +56 -0
- package/src/utils/client-secret.ts +21 -0
- package/src/utils/cookies.ts +134 -0
- package/src/utils/encoding.ts +5 -0
- package/src/utils/env-marker.ts +39 -0
- package/src/utils/identity-cookie.ts +138 -0
- package/src/utils/identity.ts +240 -0
- package/src/utils/index.ts +19 -0
- package/src/utils/issuer.ts +7 -0
- package/src/utils/openauth.ts +163 -0
- package/src/utils/pkce.ts +16 -0
- package/src/utils/state.ts +20 -0
- package/src/utils/tokens.ts +128 -0
- package/src/virtual.d.ts +5 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { createLocalJWKSet, jwtVerify } from 'jose'
|
|
2
|
+
import type { TokenRefreshResult } from '../types.js'
|
|
3
|
+
import { createClientSecretBasicAuthHeader } from './client-secret.js'
|
|
4
|
+
|
|
5
|
+
type JWKSFunction = ReturnType<typeof createLocalJWKSet>
|
|
6
|
+
|
|
7
|
+
const jwksCache = new Map<string, JWKSFunction>()
|
|
8
|
+
|
|
9
|
+
async function getJWKS(issuer: string): Promise<JWKSFunction> {
|
|
10
|
+
let jwks = jwksCache.get(issuer)
|
|
11
|
+
if (jwks) return jwks
|
|
12
|
+
|
|
13
|
+
const response = await fetch(`${issuer}/.well-known/jwks.json`)
|
|
14
|
+
if (!response.ok) throw new Error(`JWKS fetch failed: ${response.status}`)
|
|
15
|
+
const data = (await response.json()) as { keys: JsonWebKey[] }
|
|
16
|
+
jwks = createLocalJWKSet(data)
|
|
17
|
+
jwksCache.set(issuer, jwks)
|
|
18
|
+
return jwks
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface VerifiedClaims {
|
|
22
|
+
sub: string
|
|
23
|
+
email: string
|
|
24
|
+
name?: string | null
|
|
25
|
+
image?: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface TokenPayloadClaims extends VerifiedClaims {
|
|
29
|
+
isNewUser: boolean
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function extractTokenClaims(payload: Record<string, unknown>): TokenPayloadClaims {
|
|
33
|
+
const properties = (payload.properties ?? {}) as Record<string, unknown>
|
|
34
|
+
const topLevelIsNewUser = payload.isNewUser
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
sub: payload.sub as string,
|
|
38
|
+
email: (properties.email ?? payload.email) as string,
|
|
39
|
+
name: (properties.name ?? payload.name) as string | undefined,
|
|
40
|
+
image: (properties.image ?? payload.image) as string | undefined,
|
|
41
|
+
isNewUser: Boolean(properties.isNewUser ?? topLevelIsNewUser),
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export async function verifyAccessToken(
|
|
46
|
+
issuer: string,
|
|
47
|
+
token: string,
|
|
48
|
+
): Promise<VerifiedClaims | null> {
|
|
49
|
+
try {
|
|
50
|
+
const jwks = await getJWKS(issuer)
|
|
51
|
+
const { payload } = await jwtVerify(token, jwks, { issuer })
|
|
52
|
+
const claims = extractTokenClaims(payload as Record<string, unknown>)
|
|
53
|
+
return {
|
|
54
|
+
sub: claims.sub,
|
|
55
|
+
email: claims.email,
|
|
56
|
+
name: claims.name,
|
|
57
|
+
image: claims.image,
|
|
58
|
+
}
|
|
59
|
+
} catch (err) {
|
|
60
|
+
console.error('verifyAccessToken error:', err)
|
|
61
|
+
return null
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export async function refreshTokens(
|
|
66
|
+
issuer: string,
|
|
67
|
+
refreshToken: string,
|
|
68
|
+
clientId?: string,
|
|
69
|
+
clientSecret?: string,
|
|
70
|
+
resource?: string,
|
|
71
|
+
): Promise<TokenRefreshResult | null> {
|
|
72
|
+
try {
|
|
73
|
+
const params = new URLSearchParams({
|
|
74
|
+
grant_type: 'refresh_token',
|
|
75
|
+
refresh_token: refreshToken,
|
|
76
|
+
})
|
|
77
|
+
if (clientId) params.set('client_id', clientId)
|
|
78
|
+
if (resource) params.set('resource', resource)
|
|
79
|
+
|
|
80
|
+
const headers: Record<string, string> = {
|
|
81
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
82
|
+
}
|
|
83
|
+
if (clientId && clientSecret) {
|
|
84
|
+
headers.Authorization = createClientSecretBasicAuthHeader(clientId, clientSecret)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const response = await fetch(`${issuer}/token`, {
|
|
88
|
+
method: 'POST',
|
|
89
|
+
headers,
|
|
90
|
+
body: params.toString(),
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
if (!response.ok) return null
|
|
94
|
+
|
|
95
|
+
const data = (await response.json()) as {
|
|
96
|
+
access_token: string
|
|
97
|
+
refresh_token: string
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const claims = extractTokenClaims(decodeJwtPayload(data.access_token))
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
accessToken: data.access_token,
|
|
104
|
+
refreshToken: data.refresh_token,
|
|
105
|
+
claims: {
|
|
106
|
+
sub: claims.sub,
|
|
107
|
+
email: claims.email,
|
|
108
|
+
name: claims.name,
|
|
109
|
+
image: claims.image,
|
|
110
|
+
},
|
|
111
|
+
}
|
|
112
|
+
} catch {
|
|
113
|
+
return null
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function decodeJwtPayload(jwt: string): Record<string, unknown> {
|
|
118
|
+
const parts = jwt.split('.')
|
|
119
|
+
if (parts.length !== 3) return {}
|
|
120
|
+
const payload = parts[1].replace(/-/g, '+').replace(/_/g, '/')
|
|
121
|
+
const padded = payload.padEnd(payload.length + ((4 - (payload.length % 4)) % 4), '=')
|
|
122
|
+
return JSON.parse(atob(padded))
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** @internal Test-only helper. Not exported from @growth-labs/auth/utils. */
|
|
126
|
+
export function _resetJwksCache(): void {
|
|
127
|
+
jwksCache.clear()
|
|
128
|
+
}
|