cortex-auth 1.0.0 → 1.1.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.
@@ -0,0 +1,2 @@
1
+ export * from './payload-jwt';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export * from './payload-jwt';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAA"}
@@ -0,0 +1,13 @@
1
+ import { type Payload } from 'payload';
2
+ import { NextAuthRequest } from "next-auth";
3
+ export declare function authenticateRequest({ req, payload }: {
4
+ req: NextAuthRequest;
5
+ payload?: Payload;
6
+ }): Promise<{
7
+ id: string | number;
8
+ email: any;
9
+ name: any;
10
+ role: any;
11
+ method: string;
12
+ }>;
13
+ //# sourceMappingURL=authenticateRequest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authenticateRequest.d.ts","sourceRoot":"","sources":["../../src/payload-jwt/authenticateRequest.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,SAAS,CAAA;AAEtC,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAqC5C,wBAAsB,mBAAmB,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE;IAAE,GAAG,EAAE,eAAe,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE;;;;;;GAwDtG"}
@@ -0,0 +1,63 @@
1
+ import { verifySession } from "./user";
2
+ export async function authenticateRequest({ req, payload }) {
3
+ let type = 'cookie';
4
+ if (req.auth) {
5
+ const user = req.auth.user;
6
+ return {
7
+ id: user.id,
8
+ email: user.email,
9
+ name: user.name,
10
+ role: user.role,
11
+ method: 'cookie',
12
+ };
13
+ }
14
+ else {
15
+ type = 'bearer';
16
+ const session = await verifySession(req);
17
+ if (!session || !session.sub || !session.extra)
18
+ throw new Error("No valid session found");
19
+ if (!payload)
20
+ throw new Error("Payload instance is required for Keycloak user normalisation");
21
+ const payloadUser = (await payload.find({ collection: 'users', depth: 1, limit: 1, draft: false, overrideAccess: true, where: { email: { equals: session.extra.email } } })).docs[0];
22
+ if (!payloadUser && session.extra) {
23
+ // create the user in Payload
24
+ const newUser = await payload.create({
25
+ collection: 'users',
26
+ data: {
27
+ email: session.extra.email,
28
+ name: session.extra.name,
29
+ role: 'user',
30
+ enabled: true,
31
+ accounts: [
32
+ {
33
+ provider: 'keycloak', providerAccountId: session.sub, type: 'oidc',
34
+ }
35
+ ],
36
+ },
37
+ draft: false,
38
+ overrideAccess: true,
39
+ });
40
+ console.log("Created new Payload user for Keycloak user:", newUser.id, newUser.email);
41
+ return {
42
+ id: newUser.id,
43
+ email: newUser.email,
44
+ name: newUser.name,
45
+ role: newUser.role,
46
+ method: 'bearer',
47
+ };
48
+ }
49
+ else if (payloadUser) {
50
+ return {
51
+ id: payloadUser.id,
52
+ email: payloadUser.email,
53
+ name: payloadUser.name,
54
+ role: payloadUser.role,
55
+ method: 'bearer',
56
+ };
57
+ }
58
+ else {
59
+ throw new Error("No user found for the given session");
60
+ }
61
+ }
62
+ }
63
+ //# sourceMappingURL=authenticateRequest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authenticateRequest.js","sourceRoot":"","sources":["../../src/payload-jwt/authenticateRequest.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAyCvC,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,EAAE,GAAG,EAAE,OAAO,EAA+C;IACnG,IAAI,IAAI,GAAG,QAAQ,CAAA;IACnB,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;QACX,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,IAAY,CAAA;QAClC,OAAO;YACH,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,MAAM,EAAE,QAAQ;SACnB,CAAA;IACL,CAAC;SAAM,CAAC;QACJ,IAAI,GAAG,QAAQ,CAAA;QACf,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,GAAG,CAAC,CAAA;QACxC,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAA;QACzF,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,8DAA8D,CAAC,CAAA;QAC7F,MAAM,WAAW,GAAG,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QAEpL,IAAI,CAAC,WAAW,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAChC,6BAA6B;YAC7B,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC;gBACjC,UAAU,EAAE,OAAO;gBACnB,IAAI,EAAE;oBACF,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,KAAK;oBAC1B,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI;oBACxB,IAAI,EAAE,MAAM;oBACZ,OAAO,EAAE,IAAI;oBACb,QAAQ,EAAE;wBACN;4BACI,QAAQ,EAAE,UAAU,EAAE,iBAAiB,EAAE,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM;yBACrE;qBACJ;iBACJ;gBACD,KAAK,EAAE,KAAK;gBACZ,cAAc,EAAE,IAAI;aACvB,CAAC,CAAA;YACF,OAAO,CAAC,GAAG,CAAC,6CAA6C,EAAE,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,KAAK,CAAC,CAAA;YACrF,OAAO;gBACH,EAAE,EAAE,OAAO,CAAC,EAAE;gBACd,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,MAAM,EAAE,QAAQ;aACnB,CAAA;QACL,CAAC;aAAM,IAAI,WAAW,EAAE,CAAC;YACrB,OAAO;gBACH,EAAE,EAAE,WAAW,CAAC,EAAE;gBAClB,KAAK,EAAE,WAAW,CAAC,KAAK;gBACxB,IAAI,EAAE,WAAW,CAAC,IAAI;gBACtB,IAAI,EAAE,WAAW,CAAC,IAAI;gBACtB,MAAM,EAAE,QAAQ;aACnB,CAAA;QACL,CAAC;aAAM,CAAC;YACJ,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAA;QAC1D,CAAC;IACL,CAAC;AACL,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from './user';
2
+ export * from './authenticateRequest';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/payload-jwt/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAA;AACtB,cAAc,uBAAuB,CAAA"}
@@ -0,0 +1,3 @@
1
+ export * from './user';
2
+ export * from './authenticateRequest';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/payload-jwt/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAA;AACtB,cAAc,uBAAuB,CAAA"}
@@ -0,0 +1,14 @@
1
+ import type { NextRequest } from "next/server";
2
+ export interface AuthInfo {
3
+ sub: string;
4
+ exp: number;
5
+ scopes: string[];
6
+ extra?: Record<string, any>;
7
+ }
8
+ export declare function verifySession(req: NextRequest): Promise<AuthInfo | undefined>;
9
+ export declare function verifyToken(bearer: string): Promise<AuthInfo | undefined>;
10
+ /**
11
+ * Check if a token is expired
12
+ */
13
+ export declare function isTokenExpired(token: string): Promise<boolean>;
14
+ //# sourceMappingURL=user.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../src/payload-jwt/user.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,MAAM,WAAW,QAAQ;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC/B;AA0CD,wBAAsB,aAAa,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAgCnF;AAID,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CA0B/E;AAGD;;GAEG;AACH,wBAAsB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAMpE"}
@@ -0,0 +1,100 @@
1
+ import { jwtVerify, createRemoteJWKSet, decodeJwt } from "jose";
2
+ const OIDC_ISSUER = process.env.OAUTH_ISSUER; // e.g. https://login.microsoftonline.com/<tenant>/v2.0
3
+ const OAUTH_DISCOVERY_URL = `${OIDC_ISSUER}/.well-known/openid-configuration`;
4
+ // Fetch JWKS URL from OAuth well-known configuration
5
+ async function getJwksUrl() {
6
+ const wellKnownUrl = OAUTH_DISCOVERY_URL;
7
+ try {
8
+ const response = await fetch(wellKnownUrl);
9
+ const config = await response.json();
10
+ return config.jwks_uri;
11
+ }
12
+ catch (error) {
13
+ console.error("Failed to fetch well-known config:", error);
14
+ // Fallback to the hardcoded path
15
+ const fallbackUrl = `${OAUTH_DISCOVERY_URL}/discovery/v2.0/keys`;
16
+ return fallbackUrl;
17
+ }
18
+ }
19
+ // Create JWKS instance - will be initialized lazily
20
+ let JWKS = null;
21
+ async function getJWKS() {
22
+ if (!JWKS) {
23
+ const jwksUrl = await getJwksUrl();
24
+ JWKS = createRemoteJWKSet(new URL(jwksUrl));
25
+ }
26
+ return JWKS;
27
+ }
28
+ function extractBearer(req) {
29
+ const h = req.headers.get("authorization");
30
+ if (!h)
31
+ return;
32
+ const [scheme, token] = h.split(" ");
33
+ if (scheme.toLowerCase() !== "bearer")
34
+ return;
35
+ return token;
36
+ }
37
+ // Verify Next Session and return AuthInfo
38
+ export async function verifySession(req) {
39
+ const bearer = extractBearer(req);
40
+ if (bearer) {
41
+ // for development ONLY, use a dummy token
42
+ try {
43
+ const decodedJWT = decodeJwt(bearer);
44
+ // Get JWKS dynamically from well-known endpoint
45
+ const jwks = await getJWKS();
46
+ const { payload, protectedHeader } = await jwtVerify(bearer, jwks, {
47
+ issuer: OIDC_ISSUER,
48
+ algorithms: ["RS256"],
49
+ });
50
+ return {
51
+ sub: payload.sub ?? "unknown",
52
+ exp: payload.exp ?? 0,
53
+ scopes: typeof payload.scp === "string" ? payload.scp.split(" ") : [],
54
+ extra: { email: payload.email, name: payload.name },
55
+ };
56
+ }
57
+ catch (error) {
58
+ console.log("JWT verification failed:", error);
59
+ return undefined;
60
+ }
61
+ }
62
+ // fallback: NextAuth session (optional)
63
+ return undefined;
64
+ }
65
+ export async function verifyToken(bearer) {
66
+ if (bearer) {
67
+ // for development ONLY, use a dummy token
68
+ try {
69
+ const decodedJWT = decodeJwt(bearer);
70
+ // Get JWKS dynamically from well-known endpoint
71
+ const jwks = await getJWKS();
72
+ const { payload, protectedHeader } = await jwtVerify(bearer, jwks, {
73
+ issuer: OIDC_ISSUER,
74
+ algorithms: ["RS256"],
75
+ });
76
+ return {
77
+ sub: payload.sub ?? "unknown",
78
+ exp: payload.exp ?? 0,
79
+ scopes: typeof payload.scp === "string" ? payload.scp.split(" ") : [],
80
+ extra: { email: payload.email, name: payload.name },
81
+ };
82
+ }
83
+ catch (error) {
84
+ console.log("JWT verification failed:", error);
85
+ return undefined;
86
+ }
87
+ }
88
+ return undefined;
89
+ }
90
+ /**
91
+ * Check if a token is expired
92
+ */
93
+ export async function isTokenExpired(token) {
94
+ const decoded = await verifyToken(token);
95
+ if (!decoded || !decoded.exp)
96
+ return true;
97
+ const currentTime = Math.floor(Date.now() / 1000);
98
+ return decoded.exp < currentTime;
99
+ }
100
+ //# sourceMappingURL=user.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user.js","sourceRoot":"","sources":["../../src/payload-jwt/user.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cortex-auth",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Shared authentication utilities for Node.js and Next.js applications",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.esm.js",
@@ -37,7 +37,9 @@
37
37
  "jsonwebtoken": "^9.0.3"
38
38
  },
39
39
  "peerDependencies": {
40
- "next": ">=15.0.0"
40
+ "next": ">=15.0.0",
41
+ "next-auth": "5.0.0-beta.30",
42
+ "payload": "^3.70.0"
41
43
  },
42
44
  "peerDependenciesMeta": {
43
45
  "next": {
@@ -52,6 +54,8 @@
52
54
  "@typescript-eslint/parser": "^6.15.0",
53
55
  "eslint": "^8.56.0",
54
56
  "next": "^15.0.0",
57
+ "next-auth": "5.0.0-beta.30",
58
+ "payload": "^3.70.0",
55
59
  "typescript": "^5.3.3"
56
60
  }
57
61
  }