@coreiam/auth 1.0.2 → 1.0.5

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.
@@ -2,15 +2,17 @@
2
2
  // src/core/index.ts
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.CoreIAM = void 0;
5
- const DEFAULT_GATEWAY_URL = "https://api.e-qalam.com";
6
5
  class CoreIAM {
7
6
  apiKey = '';
8
7
  baseUrl = '';
9
8
  constructor(config) {
10
9
  this.apiKey = config?.apiKey || process.env.COREIAM_API_KEY || '';
11
- this.baseUrl = config?.baseUrl || DEFAULT_GATEWAY_URL;
10
+ // FIX: Removed hardcoded URL. Throw if missing.
11
+ this.baseUrl = config?.baseUrl || process.env.COREIAM_API_URL || '';
12
12
  if (!this.apiKey)
13
- throw new Error('CoreIAM: Missing API Key');
13
+ throw new Error('CoreIAM: Missing API Key (COREIAM_API_KEY)');
14
+ if (!this.baseUrl)
15
+ throw new Error('CoreIAM: Missing Gateway URL (COREIAM_API_URL)');
14
16
  }
15
17
  // Exposed publicly so the Next.js layer can use it for getSession()
16
18
  async refreshTokens(headers) {
@@ -9,8 +9,6 @@ exports.logoutActionSdk = logoutActionSdk;
9
9
  const headers_1 = require("next/headers");
10
10
  const core_1 = require("../core");
11
11
  const iam = new core_1.CoreIAM();
12
- // GLOBAL LOCK: Prevents concurrent refresh requests
13
- let refreshLock = null;
14
12
  async function syncCookiesToNext(response) {
15
13
  const cookieStore = await (0, headers_1.cookies)();
16
14
  const setCookies = response.headers.getSetCookie();
@@ -31,21 +29,17 @@ async function syncCookiesToNext(response) {
31
29
  cookieOptions.path = attrValue || '/';
32
30
  if (lowerName === 'max-age') {
33
31
  const maxAge = parseInt(attrValue, 10);
34
- if (maxAge <= 0) {
32
+ if (maxAge <= 0)
35
33
  isDeleting = true;
36
- }
37
- else {
34
+ else
38
35
  cookieOptions.maxAge = maxAge;
39
- }
40
36
  }
41
37
  if (lowerName === 'expires') {
42
38
  const expiryDate = new Date(attrValue);
43
- if (expiryDate.getTime() < Date.now()) {
39
+ if (expiryDate.getTime() < Date.now())
44
40
  isDeleting = true;
45
- }
46
- else {
41
+ else
47
42
  cookieOptions.expires = expiryDate;
48
- }
49
43
  }
50
44
  if (lowerName === 'samesite')
51
45
  cookieOptions.sameSite = attrValue?.toLowerCase() || 'lax';
@@ -62,38 +56,7 @@ async function syncCookiesToNext(response) {
62
56
  }
63
57
  async function getSession() {
64
58
  const reqHeaders = await (0, headers_1.headers)();
65
- let response = await iam.proxy('/auth/profile', { method: 'GET' }, reqHeaders);
66
- if (response.status === 401) {
67
- // 1. Use the lock! If another component is already refreshing, wait for it.
68
- if (!refreshLock) {
69
- refreshLock = iam.proxy('/auth/refresh-token', { method: 'POST' }, reqHeaders)
70
- .then(res => res.ok ? res.headers.getSetCookie() : null)
71
- .finally(() => { refreshLock = null; }); // Clear lock when done
72
- }
73
- const setCookies = await refreshLock;
74
- if (setCookies && setCookies.length > 0) {
75
- // 2. Sync the new cookies to the browser
76
- const fakeRes = new Response(null, { headers: { 'set-cookie': setCookies.join(', ') } });
77
- await syncCookiesToNext(fakeRes);
78
- // 3. Build new headers with the refreshed cookies and retry
79
- const cookieStore = await (0, headers_1.cookies)();
80
- const newJwt = cookieStore.get('jwt')?.value;
81
- const newRefresh = cookieStore.get('refreshToken')?.value;
82
- const retryHeaders = new Headers(reqHeaders);
83
- if (newJwt && newRefresh) {
84
- retryHeaders.set('cookie', `jwt=${newJwt}; refreshToken=${newRefresh}`);
85
- retryHeaders.set('Authorization', `Bearer ${newJwt}`);
86
- }
87
- response = await iam.proxy('/auth/profile', { method: 'GET' }, retryHeaders);
88
- }
89
- else {
90
- // 4. CRITICAL: If refresh failed, CLEAR THE COOKIES to break the infinite loop!
91
- const cookieStore = await (0, headers_1.cookies)();
92
- cookieStore.delete('jwt');
93
- cookieStore.delete('refreshToken');
94
- return null;
95
- }
96
- }
59
+ const response = await iam.proxy('/auth/profile', { method: 'GET' }, reqHeaders);
97
60
  if (!response.ok)
98
61
  return null;
99
62
  const coreIamIdentity = await response.json();
@@ -1,9 +1,10 @@
1
1
  export { CoreIAMProvider, useAuth } from './provider';
2
2
  export { loginActionSdk, registerActionSdk, logoutActionSdk, getSession } from './actions';
3
+ export { coreIamMiddleware } from './middleware';
3
4
  import { NextRequest, NextResponse } from 'next/server';
4
5
  import { CoreIAM } from '../core';
5
6
  export declare class NextHandlers {
6
7
  private iam;
7
- constructor(iam: CoreIAM);
8
+ constructor(iam?: CoreIAM);
8
9
  handleProxy(req: NextRequest, internalPath: string): Promise<NextResponse<any>>;
9
10
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.NextHandlers = exports.getSession = exports.logoutActionSdk = exports.registerActionSdk = exports.loginActionSdk = exports.useAuth = exports.CoreIAMProvider = void 0;
3
+ exports.NextHandlers = exports.coreIamMiddleware = exports.getSession = exports.logoutActionSdk = exports.registerActionSdk = exports.loginActionSdk = exports.useAuth = exports.CoreIAMProvider = void 0;
4
4
  // src/nextjs/index.ts
5
5
  var provider_1 = require("./provider");
6
6
  Object.defineProperty(exports, "CoreIAMProvider", { enumerable: true, get: function () { return provider_1.CoreIAMProvider; } });
@@ -10,11 +10,16 @@ Object.defineProperty(exports, "loginActionSdk", { enumerable: true, get: functi
10
10
  Object.defineProperty(exports, "registerActionSdk", { enumerable: true, get: function () { return actions_1.registerActionSdk; } });
11
11
  Object.defineProperty(exports, "logoutActionSdk", { enumerable: true, get: function () { return actions_1.logoutActionSdk; } });
12
12
  Object.defineProperty(exports, "getSession", { enumerable: true, get: function () { return actions_1.getSession; } });
13
+ // FIX: Export the new handleCoreIamAuth function instead of the old middleware
14
+ var middleware_1 = require("./middleware");
15
+ Object.defineProperty(exports, "coreIamMiddleware", { enumerable: true, get: function () { return middleware_1.coreIamMiddleware; } });
13
16
  const server_1 = require("next/server");
17
+ const core_1 = require("../core");
14
18
  class NextHandlers {
15
19
  iam;
16
20
  constructor(iam) {
17
- this.iam = iam;
21
+ // If no iam client is provided, instantiate one using the env variables
22
+ this.iam = iam || new core_1.CoreIAM({ apiKey: process.env.COREIAM_API_KEY, baseUrl: process.env.COREIAM_API_URL });
18
23
  }
19
24
  async handleProxy(req, internalPath) {
20
25
  const method = internalPath === '/csrf' ? 'POST' : req.method;
@@ -0,0 +1,9 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ /**
3
+ * Handles CoreIAM token refresh and header rewriting.
4
+ * Returns the NextResponse to return, and whether the user is authenticated.
5
+ */
6
+ export declare function coreIamMiddleware(request: NextRequest): Promise<{
7
+ response: NextResponse;
8
+ isAuthenticated: boolean;
9
+ }>;
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.coreIamMiddleware = coreIamMiddleware;
4
+ // src/nextjs/middleware.ts
5
+ const server_1 = require("next/server");
6
+ const jwt_decode_1 = require("jwt-decode");
7
+ /**
8
+ * Handles CoreIAM token refresh and header rewriting.
9
+ * Returns the NextResponse to return, and whether the user is authenticated.
10
+ */
11
+ async function coreIamMiddleware(request) {
12
+ const jwtCookie = request.cookies.get("jwt");
13
+ const refreshTokenCookie = request.cookies.get("refreshToken");
14
+ const gatewayUrl = process.env.COREIAM_API_URL;
15
+ let isAuthenticated = false;
16
+ let needsRefresh = false;
17
+ if (jwtCookie) {
18
+ try {
19
+ const decoded = (0, jwt_decode_1.jwtDecode)(jwtCookie.value);
20
+ if (decoded.exp && decoded.exp * 1000 > Date.now()) {
21
+ isAuthenticated = true;
22
+ }
23
+ else if (refreshTokenCookie) {
24
+ needsRefresh = true;
25
+ }
26
+ }
27
+ catch {
28
+ if (refreshTokenCookie)
29
+ needsRefresh = true;
30
+ }
31
+ }
32
+ else if (refreshTokenCookie) {
33
+ needsRefresh = true;
34
+ }
35
+ // 1. ATTEMPT SILENT REFRESH
36
+ if (needsRefresh && gatewayUrl) {
37
+ try {
38
+ const refreshRes = await fetch(`${gatewayUrl}/auth/refresh-token`, {
39
+ method: 'POST',
40
+ headers: {
41
+ 'X-API-Key': process.env.COREIAM_API_KEY,
42
+ 'cookie': `refreshToken=${refreshTokenCookie?.value || ''}; jwt=${jwtCookie?.value || ''}`,
43
+ },
44
+ });
45
+ if (refreshRes.ok) {
46
+ const setCookies = refreshRes.headers.getSetCookie();
47
+ if (setCookies && setCookies.length > 0) {
48
+ let newJwt = '';
49
+ let newRefreshToken = '';
50
+ setCookies.forEach(cookie => {
51
+ if (cookie.startsWith('jwt='))
52
+ newJwt = cookie.split(';')[0].split('=')[1];
53
+ if (cookie.startsWith('refreshToken='))
54
+ newRefreshToken = cookie.split(';')[0].split('=')[1];
55
+ });
56
+ if (newJwt) {
57
+ const requestHeaders = new Headers(request.headers);
58
+ requestHeaders.set('cookie', `jwt=${newJwt}; refreshToken=${newRefreshToken}`);
59
+ requestHeaders.set('Authorization', `Bearer ${newJwt}`);
60
+ const response = server_1.NextResponse.next({
61
+ request: { headers: requestHeaders },
62
+ });
63
+ setCookies.forEach(cookie => {
64
+ response.headers.append('set-cookie', cookie);
65
+ });
66
+ return { response, isAuthenticated: true };
67
+ }
68
+ }
69
+ }
70
+ }
71
+ catch (error) {
72
+ // Refresh failed, fall through to clear cookies
73
+ }
74
+ // 2. IF REFRESH FAILED, CLEAR COOKIES
75
+ const response = server_1.NextResponse.next();
76
+ response.cookies.delete('jwt');
77
+ response.cookies.delete('refreshToken');
78
+ return { response, isAuthenticated: false };
79
+ }
80
+ // 3. NO REFRESH NEEDED (Token is valid or completely missing)
81
+ return { response: server_1.NextResponse.next(), isAuthenticated };
82
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coreiam/auth",
3
- "version": "1.0.2",
3
+ "version": "1.0.5",
4
4
  "main": "./dist/core/index.js",
5
5
  "types": "./dist/core/index.d.ts",
6
6
  "exports": {
@@ -20,26 +20,43 @@
20
20
  "require": "./dist/react/index.js"
21
21
  }
22
22
  },
23
- "files": ["dist"],
23
+ "files": [
24
+ "dist"
25
+ ],
24
26
  "scripts": {
25
27
  "build": "tsc"
26
28
  },
27
- "keywords": ["coreiam", "auth", "saas", "multi-tenant", "nextjs"],
29
+ "keywords": [
30
+ "coreiam",
31
+ "auth",
32
+ "saas",
33
+ "multi-tenant",
34
+ "nextjs"
35
+ ],
28
36
  "author": "coreIAM",
29
37
  "license": "MIT",
30
- "publishConfig": { "access": "public" },
38
+ "publishConfig": {
39
+ "access": "public"
40
+ },
31
41
  "peerDependencies": {
32
42
  "next": ">=14",
33
43
  "react": ">=18"
34
44
  },
35
45
  "peerDependenciesMeta": {
36
- "next": { "optional": true },
37
- "react": { "optional": true }
46
+ "next": {
47
+ "optional": true
48
+ },
49
+ "react": {
50
+ "optional": true
51
+ }
38
52
  },
39
53
  "devDependencies": {
40
54
  "@types/react": "^19.2.7",
41
55
  "next": "^16.2.11",
42
56
  "react": "^19.2.7",
43
57
  "typescript": "^5.0.0"
58
+ },
59
+ "dependencies": {
60
+ "jwt-decode": "^4.0.0"
44
61
  }
45
62
  }