@ghuri/oauth 0.0.0-stage → 0.1.0-alpha.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/LICENSE +21 -0
- package/README.md +149 -2
- package/dist/chunk-IS4XX7EU.js +68 -0
- package/dist/chunk-IS4XX7EU.js.map +1 -0
- package/dist/chunk-OIBPPYBX.js +112 -0
- package/dist/chunk-OIBPPYBX.js.map +1 -0
- package/dist/chunk-TSKPVGEC.js +717 -0
- package/dist/chunk-TSKPVGEC.js.map +1 -0
- package/dist/index.d.ts +338 -0
- package/dist/index.js +228 -0
- package/dist/index.js.map +1 -0
- package/dist/rest.d.ts +18 -0
- package/dist/rest.js +33 -0
- package/dist/rest.js.map +1 -0
- package/dist/strategy-nxNiKJFS.d.ts +109 -0
- package/dist/testing.d.ts +68 -0
- package/dist/testing.js +191 -0
- package/dist/testing.js.map +1 -0
- package/package.json +51 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/testing.ts"],"sourcesContent":["// @ghuri/oauth/testing — a fake identity provider for tests (no network), and a helper that follows the round trip.\nimport { b64url, randomToken, sha256 } from './crypto'\nimport { setProviderFetch } from './http'\nimport { clearJwksCache, signJwt } from './jwt'\nimport { clearDiscoveryCache } from './oidc'\n\nexport interface MockProviderOptions {\n /** Base URL of the fake provider (default `https://provider.test`); its OIDC issuer. */\n issuer?: string\n /** Claims / userinfo document of the user who \"logs in\" (OIDC style: `sub`, `email`, …). */\n profile?: Record<string, any>\n /** Extra fields for token responses. */\n tokenResponse?: Record<string, unknown>\n /** Extra URLs answered for authenticated GETs (provider APIs such as `https://api.github.com/user`). */\n routes?: Record<string, unknown | ((req: Request) => unknown | Promise<unknown>)>\n /** Treat any token endpoint POST as this provider's (default true). */\n anyTokenEndpoint?: boolean\n}\n\ninterface IssuedCode {\n clientId: string\n redirectUri: string\n nonce?: string\n challenge?: string\n scope: string[]\n}\n\nfunction json(data: unknown, status = 200): Response {\n return new Response(JSON.stringify(data), { status, headers: { 'content-type': 'application/json' } })\n}\n\n/**\n * ```ts\n * const provider = mockOAuthProvider({ profile: { sub: '42', email: 'ann@example.com', email_verified: true } })\n * const res = await followOAuth(client, '/login/acme/', provider)\n * provider.restore()\n * ```\n */\nexport class MockProvider {\n readonly issuer: string\n profile: Record<string, any>\n readonly requests: Array<{ method: string; url: string }> = []\n private readonly codes = new Map<string, IssuedCode>()\n private readonly accessTokens = new Set<string>()\n private keys?: Promise<{ privateKey: CryptoKey; jwk: JsonWebKey & { kid: string } }>\n\n constructor(readonly options: MockProviderOptions = {}) {\n this.issuer = (options.issuer ?? 'https://provider.test').replace(/\\/+$/, '')\n this.profile = options.profile ?? {\n sub: '1',\n email: 'user@example.com',\n email_verified: true,\n name: 'Test User',\n }\n setProviderFetch((input, init) => this.handle(new Request(input as any, init)))\n }\n\n get endpoints() {\n return {\n authorization: `${this.issuer}/authorize`,\n token: `${this.issuer}/token`,\n userinfo: `${this.issuer}/userinfo`,\n jwks: `${this.issuer}/jwks`,\n revocation: `${this.issuer}/revoke`,\n }\n }\n\n private signingKey() {\n this.keys ??= (async () => {\n const pair = (await crypto.subtle.generateKey({ name: 'ECDSA', namedCurve: 'P-256' }, true, [\n 'sign',\n 'verify',\n ])) as CryptoKeyPair\n const jwk = (await crypto.subtle.exportKey('jwk', pair.publicKey)) as JsonWebKey\n return { privateKey: pair.privateKey, jwk: { ...jwk, kid: 'mock-1', use: 'sig', alg: 'ES256' } }\n })()\n return this.keys\n }\n\n /**\n * Act as the user approving the request at `authorizeUrl`: returns the callback URL (with `code` and `state`, or\n * `error=access_denied` when `deny`).\n */\n authorize(authorizeUrl: string, opts: { deny?: boolean } = {}): string {\n const u = new URL(authorizeUrl)\n const q = u.searchParams\n const redirectUri = q.get('redirect_uri')\n if (!redirectUri) throw new Error('The authorization URL has no redirect_uri.')\n const back = new URL(redirectUri)\n if (q.get('state')) back.searchParams.set('state', q.get('state')!)\n if (opts.deny) {\n back.searchParams.set('error', 'access_denied')\n return back.toString()\n }\n const code = randomToken(16)\n this.codes.set(code, {\n clientId: q.get('client_id') ?? '',\n redirectUri,\n nonce: q.get('nonce') ?? undefined,\n challenge: q.get('code_challenge') ?? undefined,\n scope: (q.get('scope') ?? '').split(/[ ,]+/).filter(Boolean),\n })\n back.searchParams.set('code', code)\n return back.toString()\n }\n\n private async tokenResponse(form: URLSearchParams): Promise<Response> {\n const code = form.get('code') ?? ''\n const issued = this.codes.get(code)\n if (!issued) return json({ error: 'invalid_grant', error_description: 'Unknown or used code.' }, 400)\n this.codes.delete(code)\n if (form.get('redirect_uri') !== issued.redirectUri)\n return json({ error: 'invalid_grant', error_description: 'redirect_uri mismatch' }, 400)\n if (issued.challenge) {\n const verifier = form.get('code_verifier') ?? ''\n if (b64url(await sha256(verifier)) !== issued.challenge) {\n return json({ error: 'invalid_grant', error_description: 'PKCE verification failed' }, 400)\n }\n }\n const accessToken = randomToken(16)\n this.accessTokens.add(accessToken)\n const body: Record<string, unknown> = {\n access_token: accessToken,\n token_type: 'Bearer',\n expires_in: 3600,\n refresh_token: randomToken(16),\n scope: issued.scope.join(' '),\n ...this.options.tokenResponse,\n }\n if (issued.scope.includes('openid')) {\n const now = Math.floor(Date.now() / 1000)\n const { privateKey } = await this.signingKey()\n body.id_token = await signJwt(\n {\n iss: this.issuer,\n aud: issued.clientId,\n iat: now,\n exp: now + 600,\n ...(issued.nonce ? { nonce: issued.nonce } : {}),\n ...this.profile,\n sub: String(this.profile.sub ?? this.profile.id),\n },\n privateKey,\n 'ES256',\n { kid: 'mock-1' },\n )\n }\n return json(body)\n }\n\n private authed(req: Request): boolean {\n const m = /^Bearer\\s+(\\S+)$/i.exec(req.headers.get('authorization') ?? '')\n return !!m && this.accessTokens.has(m[1])\n }\n\n private async handle(req: Request): Promise<Response> {\n this.requests.push({ method: req.method, url: req.url })\n const url = req.url.split('?')[0]\n if (req.method === 'POST') {\n const form = new URLSearchParams(await req.text())\n if (url === `${this.issuer}/revoke`) return new Response(null, { status: 200 })\n if (\n url === this.endpoints.token ||\n (this.options.anyTokenEndpoint !== false && form.get('grant_type'))\n ) {\n if (form.get('grant_type') === 'refresh_token') {\n const accessToken = randomToken(16)\n this.accessTokens.add(accessToken)\n return json({ access_token: accessToken, token_type: 'Bearer', expires_in: 3600 })\n }\n return this.tokenResponse(form)\n }\n }\n if (url === `${this.issuer}/.well-known/openid-configuration`) {\n return json({\n issuer: this.issuer,\n authorization_endpoint: this.endpoints.authorization,\n token_endpoint: this.endpoints.token,\n userinfo_endpoint: this.endpoints.userinfo,\n jwks_uri: this.endpoints.jwks,\n revocation_endpoint: this.endpoints.revocation,\n })\n }\n if (url === this.endpoints.jwks) return json({ keys: [(await this.signingKey()).jwk] })\n const route = this.options.routes?.[url] ?? this.options.routes?.[req.url]\n if (route !== undefined || url === this.endpoints.userinfo) {\n if (!this.authed(req)) return json({ error: 'invalid_token' }, 401)\n const value =\n route === undefined ? this.profile : typeof route === 'function' ? await (route as any)(req) : route\n return value instanceof Response ? value : json(value)\n }\n return json({ error: 'not_found', url: req.url }, 404)\n }\n\n /** Stop intercepting provider requests and forget cached discovery documents and keys. */\n restore(): void {\n setProviderFetch(undefined)\n clearDiscoveryCache()\n clearJwksCache()\n }\n}\n\nexport function mockOAuthProvider(options: MockProviderOptions = {}): MockProvider {\n clearDiscoveryCache()\n clearJwksCache()\n return new MockProvider(options)\n}\n\ninterface TestClientLike {\n get(path: string, opts?: any): Promise<Response>\n post?(path: string, opts?: any): Promise<Response>\n}\n\nfunction pathOf(url: string): string {\n const u = new URL(url, 'http://testserver')\n return `${u.pathname}${u.search}`\n}\n\n/**\n * Run the redirect round trip with the core test client: request `startPath` (GET, or POST with `method`), approve\n * at the mock provider, then request the callback. Returns the callback's response.\n */\nexport async function followOAuth(\n client: TestClientLike,\n startPath: string,\n provider: MockProvider,\n opts: { deny?: boolean; method?: 'GET' | 'POST'; body?: Record<string, unknown> } = {},\n): Promise<Response> {\n const start =\n opts.method === 'POST' && client.post\n ? await client.post(startPath, { form: opts.body ?? {} })\n : await client.get(startPath)\n const location = start.headers.get('location')\n if (start.status < 300 || start.status >= 400 || !location) {\n throw new Error(`Expected a redirect to the provider from ${startPath}, got ${start.status}.`)\n }\n return client.get(pathOf(provider.authorize(location, opts)))\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,53 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ghuri/oauth",
|
|
3
|
-
"version": "0.0.0
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0-alpha.0",
|
|
4
|
+
"description": "Ghuri OAuth: Passport.js-style login strategies (OAuth2, OpenID Connect, local, magic link, bearer) for Workers and Node",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Ghuri contributors",
|
|
8
|
+
"homepage": "https://github.com/ghurijs/oauth#readme",
|
|
9
|
+
"repository": { "type": "git", "url": "git+https://github.com/ghurijs/oauth.git" },
|
|
10
|
+
"bugs": { "url": "https://github.com/ghurijs/oauth/issues" },
|
|
11
|
+
"keywords": ["ghuri", "django", "oauth", "oauth2", "openid-connect", "oidc", "passport", "authentication", "cloudflare", "workers", "hono"],
|
|
12
|
+
"exports": {
|
|
13
|
+
".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" },
|
|
14
|
+
"./rest": { "types": "./dist/rest.d.ts", "import": "./dist/rest.js" },
|
|
15
|
+
"./testing": { "types": "./dist/testing.d.ts", "import": "./dist/testing.js" }
|
|
16
|
+
},
|
|
17
|
+
"main": "./dist/index.js",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"files": ["dist", "README.md", "LICENSE"],
|
|
20
|
+
"engines": { "node": ">=20" },
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsup",
|
|
23
|
+
"dev": "tsup --watch",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"test:watch": "vitest",
|
|
26
|
+
"typecheck": "tsc --noEmit",
|
|
27
|
+
"lint": "biome check src test",
|
|
28
|
+
"format": "biome format --write src test"
|
|
29
|
+
},
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"@ghuri/auth": "^0.1.0-alpha.1",
|
|
32
|
+
"@ghuri/core": "^0.1.0-alpha.1",
|
|
33
|
+
"@ghuri/rest": "^0.1.0-alpha.0",
|
|
34
|
+
"hono": "^4.13.0"
|
|
35
|
+
},
|
|
36
|
+
"peerDependenciesMeta": { "@ghuri/rest": { "optional": true } },
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@biomejs/biome": "^2.6.6",
|
|
39
|
+
"@ghuri/auth": "^0.1.0-alpha.1",
|
|
40
|
+
"@ghuri/core": "^0.1.0-alpha.1",
|
|
41
|
+
"@ghuri/orm": "^0.1.0-alpha.1",
|
|
42
|
+
"@ghuri/rest": "^0.1.0-alpha.0",
|
|
43
|
+
"@types/node": "^24.0.0",
|
|
44
|
+
"better-sqlite3": "^13.1.1",
|
|
45
|
+
"esbuild": "^0.27.7",
|
|
46
|
+
"hono": "^4.13.0",
|
|
47
|
+
"miniflare": "^4.20260730.0",
|
|
48
|
+
"tsup": "^8.5.1",
|
|
49
|
+
"typescript": "~5.9.3",
|
|
50
|
+
"vitest": "^5.1.0"
|
|
51
|
+
},
|
|
52
|
+
"publishConfig": { "access": "public", "tag": "next" }
|
|
53
|
+
}
|