@omg-dev/auth 0.4.24
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.mjs +43 -0
- package/package.json +28 -0
- package/src/index.ts +77 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { createRemoteJWKSet, jwtVerify } from "jose";
|
|
2
|
+
//#region src/index.ts
|
|
3
|
+
const DEFAULT_JWKS_URL = "https://auth.omg.dev/.well-known/jwks.json";
|
|
4
|
+
const DEFAULT_ISSUER = "https://auth.omg.dev";
|
|
5
|
+
/**
|
|
6
|
+
* Creates an auth middleware for the given mode.
|
|
7
|
+
* - "vibes": verifies JWT Bearer token via JWKS endpoint
|
|
8
|
+
* - "local": returns null (unauthenticated / no auth required)
|
|
9
|
+
*/
|
|
10
|
+
function createAuthMiddleware(mode, options) {
|
|
11
|
+
if (mode === "local") return async () => null;
|
|
12
|
+
const jwksUrl = options?.jwksUrl ?? DEFAULT_JWKS_URL;
|
|
13
|
+
const issuer = options?.issuer ?? DEFAULT_ISSUER;
|
|
14
|
+
const audience = options?.audience;
|
|
15
|
+
const algorithms = options?.algorithms ?? ["ES256"];
|
|
16
|
+
const JWKS = createRemoteJWKSet(new URL(jwksUrl));
|
|
17
|
+
return async (req) => {
|
|
18
|
+
const authHeader = req.headers.get("authorization") ?? "";
|
|
19
|
+
if (!authHeader.startsWith("Bearer ")) return null;
|
|
20
|
+
const token = authHeader.slice(7).trim();
|
|
21
|
+
if (!token) return null;
|
|
22
|
+
try {
|
|
23
|
+
const { payload } = await jwtVerify(token, JWKS, {
|
|
24
|
+
algorithms,
|
|
25
|
+
issuer,
|
|
26
|
+
...audience ? { audience } : {}
|
|
27
|
+
});
|
|
28
|
+
const userId = payload.sub;
|
|
29
|
+
if (!userId) return null;
|
|
30
|
+
if (userId.startsWith("svc:")) return null;
|
|
31
|
+
return {
|
|
32
|
+
userId,
|
|
33
|
+
userEmail: payload.email,
|
|
34
|
+
userName: payload.name,
|
|
35
|
+
appId: payload.appId
|
|
36
|
+
};
|
|
37
|
+
} catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
export { createAuthMiddleware };
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@omg-dev/auth",
|
|
3
|
+
"version": "0.4.24",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"types": "./src/index.ts",
|
|
8
|
+
"default": "./dist/index.mjs"
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"jose": "^5.0.0"
|
|
13
|
+
},
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/BennyKok/vibes.git"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://docs.omg.dev",
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"src"
|
|
23
|
+
],
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public",
|
|
26
|
+
"registry": "https://registry.npmjs.org/"
|
|
27
|
+
}
|
|
28
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// @omg-dev/auth — Server-side JWT verification middleware
|
|
2
|
+
//
|
|
3
|
+
// Verifies JWT tokens issued by auth.omg.dev using JWKS.
|
|
4
|
+
|
|
5
|
+
import { createRemoteJWKSet, jwtVerify, type JWTPayload } from "jose"
|
|
6
|
+
|
|
7
|
+
export type AuthMode = "vibes" | "local"
|
|
8
|
+
|
|
9
|
+
export interface AuthResult {
|
|
10
|
+
userId: string
|
|
11
|
+
userEmail?: string
|
|
12
|
+
userName?: string
|
|
13
|
+
appId?: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface VibesJWTPayload extends JWTPayload {
|
|
17
|
+
email?: string
|
|
18
|
+
name?: string
|
|
19
|
+
appId?: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const DEFAULT_JWKS_URL = "https://auth.omg.dev/.well-known/jwks.json"
|
|
23
|
+
const DEFAULT_ISSUER = "https://auth.omg.dev"
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Creates an auth middleware for the given mode.
|
|
27
|
+
* - "vibes": verifies JWT Bearer token via JWKS endpoint
|
|
28
|
+
* - "local": returns null (unauthenticated / no auth required)
|
|
29
|
+
*/
|
|
30
|
+
export function createAuthMiddleware(
|
|
31
|
+
mode: AuthMode,
|
|
32
|
+
options?: { jwksUrl?: string; issuer?: string; audience?: string | string[]; algorithms?: string[] }
|
|
33
|
+
): (req: Request) => Promise<AuthResult | null> {
|
|
34
|
+
if (mode === "local") {
|
|
35
|
+
return async () => null
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const jwksUrl = options?.jwksUrl ?? DEFAULT_JWKS_URL
|
|
39
|
+
const issuer = options?.issuer ?? DEFAULT_ISSUER
|
|
40
|
+
const audience = options?.audience
|
|
41
|
+
// Default to ES256 (auth.omg.dev's /token-minted JWTs). The OAuth provider
|
|
42
|
+
// at /api/auth/jwks signs access tokens with EdDSA, so callers verifying
|
|
43
|
+
// those must pass algorithms: ["EdDSA"] + the matching jwksUrl.
|
|
44
|
+
const algorithms = options?.algorithms ?? ["ES256"]
|
|
45
|
+
const JWKS = createRemoteJWKSet(new URL(jwksUrl))
|
|
46
|
+
|
|
47
|
+
return async (req: Request): Promise<AuthResult | null> => {
|
|
48
|
+
const authHeader = req.headers.get("authorization") ?? ""
|
|
49
|
+
if (!authHeader.startsWith("Bearer ")) return null
|
|
50
|
+
|
|
51
|
+
const token = authHeader.slice(7).trim()
|
|
52
|
+
if (!token) return null
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
const { payload } = await jwtVerify(token, JWKS, {
|
|
56
|
+
algorithms,
|
|
57
|
+
issuer,
|
|
58
|
+
...(audience ? { audience } : {}),
|
|
59
|
+
}) as { payload: VibesJWTPayload }
|
|
60
|
+
|
|
61
|
+
const userId = payload.sub
|
|
62
|
+
if (!userId) return null
|
|
63
|
+
// Service-principal tokens (sub="svc:*") are minted for infra-to-infra
|
|
64
|
+
// calls; user-facing apps must never accept them as a logged-in user.
|
|
65
|
+
if (userId.startsWith("svc:")) return null
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
userId,
|
|
69
|
+
userEmail: payload.email,
|
|
70
|
+
userName: payload.name,
|
|
71
|
+
appId: payload.appId,
|
|
72
|
+
}
|
|
73
|
+
} catch {
|
|
74
|
+
return null
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|