@loka-sms/sso 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.
@@ -54,7 +54,13 @@ function useOAuthCallback(input) {
54
54
  method: 'POST',
55
55
  headers: { 'Content-Type': 'application/json' },
56
56
  credentials: 'include',
57
- body: JSON.stringify({ grant_type: 'authorization_code', code, code_verifier: codeVerifier }),
57
+ body: JSON.stringify({
58
+ grant_type: 'authorization_code',
59
+ code,
60
+ code_verifier: codeVerifier,
61
+ client_id: input.clientId,
62
+ redirect_uri: `${window.location.origin}${window.location.pathname}`,
63
+ }),
58
64
  });
59
65
  // Development fallback: retry without code_verifier if PKCE fails
60
66
  if (!res.ok && !codeVerifier) {
@@ -62,7 +68,7 @@ function useOAuthCallback(input) {
62
68
  method: 'POST',
63
69
  headers: { 'Content-Type': 'application/json' },
64
70
  credentials: 'include',
65
- body: JSON.stringify({ grant_type: 'authorization_code', code }),
71
+ body: JSON.stringify({ grant_type: 'authorization_code', code, client_id: input.clientId, redirect_uri: `${window.location.origin}${window.location.pathname}` }),
66
72
  });
67
73
  }
68
74
  if (!res.ok)
@@ -78,23 +84,11 @@ function useOAuthCallback(input) {
78
84
  }
79
85
  // 1. Save token from JWT decode FIRST — no network needed
80
86
  saveJwt(accessToken, refreshTok);
81
- // 2. Redirect immediately — don't wait for API calls
82
- setState({ error: null, loading: false, phase: 'done' });
83
- window.location.href = input.redirectPath || '/';
84
- }
85
- catch {
86
- // Even if token exchange fails, saved token from JWT decode might still exist
87
- if (accessToken)
88
- saveJwt(accessToken, refreshTok);
89
- setState({ error: 'Gagal menyelesaikan login. Silakan coba lagi.', loading: false, phase: 'error' });
90
- }
91
- // 3. API calls — fire-and-forget (non-blocking)
92
- if (accessToken) {
93
- fetch(`${apiBase}/auth/set-cookie`, {
87
+ await fetch(`${apiBase}/auth/set-cookie`, {
94
88
  method: 'POST', headers: { 'Content-Type': 'application/json' },
95
89
  credentials: 'include',
96
90
  body: JSON.stringify({ token: accessToken, refreshToken: refreshTok }),
97
- }).catch(() => { });
91
+ });
98
92
  fetch(`${apiBase}/auth/me`, {
99
93
  headers: { Authorization: `Bearer ${accessToken}` },
100
94
  credentials: 'include',
@@ -105,6 +99,15 @@ function useOAuthCallback(input) {
105
99
  document.cookie = `sms_user_profile=${encodeURIComponent(JSON.stringify(p))}; path=/; SameSite=Lax`;
106
100
  }
107
101
  }).catch(() => { });
102
+ // 2. Redirect after Gateway cookies are persisted.
103
+ setState({ error: null, loading: false, phase: 'done' });
104
+ window.location.href = input.redirectPath || '/';
105
+ }
106
+ catch {
107
+ // Even if token exchange fails, saved token from JWT decode might still exist
108
+ if (accessToken)
109
+ saveJwt(accessToken, refreshTok);
110
+ setState({ error: 'Gagal menyelesaikan login. Silakan coba lagi.', loading: false, phase: 'error' });
108
111
  }
109
112
  })();
110
113
  }, []);
package/dist/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  export { sha256 } from './sha256';
2
2
  export { generateCodeVerifier, generateCodeChallenge, generateState, } from './pkce';
3
+ export { hasSsoToken, redirectToOAuthLogin } from './oauthRedirect';
4
+ export type { OAuthRedirectOptions } from './oauthRedirect';
3
5
  export { SSO_STORAGE_KEYS, SSO_CHANNELS, API_HEADERS, } from './constants';
4
6
  export { createAuthInterceptor } from './interceptor';
5
7
  export { useOAuthCallback } from './hooks/useOAuthCallback';
package/dist/index.js CHANGED
@@ -1,12 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.OAuthTransfer = exports.OAuthCallback = exports.useCrossAppLogout = exports.useOAuthCallback = exports.createAuthInterceptor = exports.API_HEADERS = exports.SSO_CHANNELS = exports.SSO_STORAGE_KEYS = exports.generateState = exports.generateCodeChallenge = exports.generateCodeVerifier = exports.sha256 = void 0;
3
+ exports.OAuthTransfer = exports.OAuthCallback = exports.useCrossAppLogout = exports.useOAuthCallback = exports.createAuthInterceptor = exports.API_HEADERS = exports.SSO_CHANNELS = exports.SSO_STORAGE_KEYS = exports.redirectToOAuthLogin = exports.hasSsoToken = exports.generateState = exports.generateCodeChallenge = exports.generateCodeVerifier = exports.sha256 = void 0;
4
4
  var sha256_1 = require("./sha256");
5
5
  Object.defineProperty(exports, "sha256", { enumerable: true, get: function () { return sha256_1.sha256; } });
6
6
  var pkce_1 = require("./pkce");
7
7
  Object.defineProperty(exports, "generateCodeVerifier", { enumerable: true, get: function () { return pkce_1.generateCodeVerifier; } });
8
8
  Object.defineProperty(exports, "generateCodeChallenge", { enumerable: true, get: function () { return pkce_1.generateCodeChallenge; } });
9
9
  Object.defineProperty(exports, "generateState", { enumerable: true, get: function () { return pkce_1.generateState; } });
10
+ var oauthRedirect_1 = require("./oauthRedirect");
11
+ Object.defineProperty(exports, "hasSsoToken", { enumerable: true, get: function () { return oauthRedirect_1.hasSsoToken; } });
12
+ Object.defineProperty(exports, "redirectToOAuthLogin", { enumerable: true, get: function () { return oauthRedirect_1.redirectToOAuthLogin; } });
10
13
  var constants_1 = require("./constants");
11
14
  Object.defineProperty(exports, "SSO_STORAGE_KEYS", { enumerable: true, get: function () { return constants_1.SSO_STORAGE_KEYS; } });
12
15
  Object.defineProperty(exports, "SSO_CHANNELS", { enumerable: true, get: function () { return constants_1.SSO_CHANNELS; } });
@@ -0,0 +1,8 @@
1
+ export interface OAuthRedirectOptions {
2
+ clientId: string;
3
+ apiBase?: string;
4
+ callbackPath?: string;
5
+ authenticatedPath?: string;
6
+ }
7
+ export declare function hasSsoToken(): boolean;
8
+ export declare function redirectToOAuthLogin({ clientId, apiBase, callbackPath, authenticatedPath, }: OAuthRedirectOptions): Promise<void>;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hasSsoToken = hasSsoToken;
4
+ exports.redirectToOAuthLogin = redirectToOAuthLogin;
5
+ const pkce_1 = require("./pkce");
6
+ function trimTrailingSlash(value) {
7
+ return value.replace(/\/+$/, '');
8
+ }
9
+ function getCookie(name) {
10
+ const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
11
+ const match = document.cookie.match(new RegExp(`(^| )${escaped}=([^;]+)`));
12
+ return match ? decodeURIComponent(match[2]) : '';
13
+ }
14
+ function hasSsoToken() {
15
+ return Boolean(getCookie('sms_ac_token') || localStorage.getItem('sms_ac_token'));
16
+ }
17
+ async function redirectToOAuthLogin({ clientId, apiBase = '/api', callbackPath = '/auth/callback', authenticatedPath = '/', }) {
18
+ if (hasSsoToken()) {
19
+ window.location.href = authenticatedPath;
20
+ return;
21
+ }
22
+ const state = (0, pkce_1.generateState)();
23
+ const codeVerifier = (0, pkce_1.generateCodeVerifier)();
24
+ const codeChallenge = await (0, pkce_1.generateCodeChallenge)(codeVerifier);
25
+ sessionStorage.setItem(`oauth_verifier_${state}`, codeVerifier);
26
+ const callbackUrl = `${window.location.origin}${callbackPath}`;
27
+ const authorizeUrl = new URL(`${trimTrailingSlash(apiBase)}/oauth/authorize`);
28
+ authorizeUrl.searchParams.set('client_id', clientId);
29
+ authorizeUrl.searchParams.set('redirect_uri', callbackUrl);
30
+ authorizeUrl.searchParams.set('response_type', 'code');
31
+ authorizeUrl.searchParams.set('state', state);
32
+ authorizeUrl.searchParams.set('code_challenge', codeChallenge);
33
+ authorizeUrl.searchParams.set('code_challenge_method', 'S256');
34
+ window.location.href = authorizeUrl.toString();
35
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loka-sms/sso",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "SSO utilities, hooks, and components for Loka SMS modules (OAuth2 PKCE, cross-app logout)",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",