@cemiar/auth-sdk 1.0.0 → 1.0.1
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/browser/CemiarAuthClient.d.ts +11 -3
- package/dist/browser/CemiarAuthClient.js +68 -17
- package/dist/shared/Types.d.ts +3 -2
- package/dist/shared/Utils.d.ts +2 -0
- package/dist/shared/Utils.js +6 -0
- package/dist/shared/models/Claim.d.ts +13 -0
- package/dist/shared/models/Claim.js +7 -0
- package/package.json +1 -1
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { AxiosInstance } from "axios";
|
|
2
|
-
import type { AuthTokens, CemiarAuthClientConfig, CemiarAuthClientInstance, CreateApiClientOptions, EmailCodeResponse, LogoutOptions
|
|
2
|
+
import type { AuthTokens, CemiarAuthClientConfig, CemiarAuthClientInstance, CreateApiClientOptions, EmailCodeResponse, LogoutOptions } from "../shared/Types.js";
|
|
3
|
+
export interface UserInfo {
|
|
4
|
+
email: string;
|
|
5
|
+
displayName?: string;
|
|
6
|
+
}
|
|
3
7
|
export declare class CemiarAuthClient implements CemiarAuthClientInstance {
|
|
4
8
|
private readonly baseUrl;
|
|
5
9
|
private readonly tenantId;
|
|
@@ -9,15 +13,16 @@ export declare class CemiarAuthClient implements CemiarAuthClientInstance {
|
|
|
9
13
|
private readonly onTokenChange?;
|
|
10
14
|
private readonly onAuthFailure?;
|
|
11
15
|
private readonly logoutRedirectUrl?;
|
|
16
|
+
private loginMethod;
|
|
12
17
|
private refreshPromise;
|
|
13
18
|
constructor(config: CemiarAuthClientConfig);
|
|
14
19
|
getAccessToken(): string | null;
|
|
15
20
|
setAccessToken(token: string | null): void;
|
|
16
21
|
private authPost;
|
|
17
22
|
sendEmailCode(email: string): Promise<EmailCodeResponse>;
|
|
18
|
-
verifyEmailCode(email: string, code: string): Promise<
|
|
23
|
+
verifyEmailCode(email: string, code: string): Promise<UserInfo | null>;
|
|
24
|
+
handleRedirectMicrosoftCallback(routeQueryData: Record<string, string>): UserInfo | null;
|
|
19
25
|
getMicrosoftLoginUrl(extraParams?: Record<string, string>): string;
|
|
20
|
-
exchangeMicrosoftCode(code: string): Promise<AuthTokens>;
|
|
21
26
|
refreshToken(): Promise<AuthTokens>;
|
|
22
27
|
logout(options?: LogoutOptions): Promise<void>;
|
|
23
28
|
createApiClient(options: CreateApiClientOptions): AxiosInstance;
|
|
@@ -26,5 +31,8 @@ export declare class CemiarAuthClient implements CemiarAuthClientInstance {
|
|
|
26
31
|
private handleResponseError;
|
|
27
32
|
private queueTokenRefresh;
|
|
28
33
|
private handleAuthFailure;
|
|
34
|
+
private loadLoginMethodFromStorage;
|
|
35
|
+
private persistLoginMethod;
|
|
36
|
+
private getCurrentLoginMethod;
|
|
29
37
|
}
|
|
30
38
|
export declare function createCemiarAuthClient(config: CemiarAuthClientConfig): CemiarAuthClientInstance;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import axios from "axios";
|
|
2
2
|
import { createDefaultTokenStorage } from "./Storage.js";
|
|
3
|
-
import { normalizeBaseUrl, isBrowser } from "../shared/Utils.js";
|
|
3
|
+
import { normalizeBaseUrl, isBrowser, tokenToClaim } from "../shared/Utils.js";
|
|
4
|
+
const LOGIN_METHOD_STORAGE_KEY = "cemiar-auth-login-method";
|
|
4
5
|
function parseAuds(auds) {
|
|
5
6
|
return auds.map(a => a.trim()).filter(Boolean);
|
|
6
7
|
}
|
|
@@ -27,6 +28,7 @@ export class CemiarAuthClient {
|
|
|
27
28
|
this.logoutRedirectUrl =
|
|
28
29
|
(_b = config.logoutRedirectUrl) !== null && _b !== void 0 ? _b : (isBrowser ? `${window.location.origin}/login` : undefined);
|
|
29
30
|
this.storage = (_c = config.storage) !== null && _c !== void 0 ? _c : createDefaultTokenStorage();
|
|
31
|
+
this.loginMethod = this.loadLoginMethodFromStorage();
|
|
30
32
|
}
|
|
31
33
|
getAccessToken() {
|
|
32
34
|
return this.storage.getToken();
|
|
@@ -34,6 +36,9 @@ export class CemiarAuthClient {
|
|
|
34
36
|
setAccessToken(token) {
|
|
35
37
|
var _a;
|
|
36
38
|
this.storage.setToken(token);
|
|
39
|
+
if (token === null) {
|
|
40
|
+
this.persistLoginMethod(null);
|
|
41
|
+
}
|
|
37
42
|
(_a = this.onTokenChange) === null || _a === void 0 ? void 0 : _a.call(this, token);
|
|
38
43
|
}
|
|
39
44
|
async authPost(path, body, withCredentials = true) {
|
|
@@ -56,14 +61,28 @@ export class CemiarAuthClient {
|
|
|
56
61
|
const data = await this.authPost("/emailCodes/validate", payload);
|
|
57
62
|
const accessToken = extractAccessToken(data);
|
|
58
63
|
this.setAccessToken(accessToken);
|
|
64
|
+
this.persistLoginMethod("emailCode");
|
|
65
|
+
const claim = tokenToClaim(accessToken);
|
|
59
66
|
return {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
blacklisted: Boolean(data.blacklisted),
|
|
63
|
-
raw: data
|
|
67
|
+
email: claim.sub,
|
|
68
|
+
displayName: claim.displayName
|
|
64
69
|
};
|
|
65
70
|
}
|
|
71
|
+
handleRedirectMicrosoftCallback(routeQueryData) {
|
|
72
|
+
const token = routeQueryData["token"];
|
|
73
|
+
if (token) {
|
|
74
|
+
this.setAccessToken(token);
|
|
75
|
+
this.persistLoginMethod("microsoft");
|
|
76
|
+
const claim = tokenToClaim(token);
|
|
77
|
+
return {
|
|
78
|
+
email: claim.sub,
|
|
79
|
+
displayName: claim.displayName
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
66
84
|
getMicrosoftLoginUrl(extraParams = {}) {
|
|
85
|
+
this.persistLoginMethod("microsoft");
|
|
67
86
|
const params = new URLSearchParams({
|
|
68
87
|
tenantId: this.tenantId,
|
|
69
88
|
auds: this.auds.join(","),
|
|
@@ -74,16 +93,6 @@ export class CemiarAuthClient {
|
|
|
74
93
|
}
|
|
75
94
|
return `${this.baseUrl}/microsoft?${params.toString()}`;
|
|
76
95
|
}
|
|
77
|
-
async exchangeMicrosoftCode(code) {
|
|
78
|
-
const payload = {
|
|
79
|
-
code,
|
|
80
|
-
...(this.redirectUrl ? { redirectUrl: this.redirectUrl } : {})
|
|
81
|
-
};
|
|
82
|
-
const data = await this.authPost("/microsoft/callback", payload);
|
|
83
|
-
const accessToken = extractAccessToken(data);
|
|
84
|
-
this.setAccessToken(accessToken);
|
|
85
|
-
return { accessToken };
|
|
86
|
-
}
|
|
87
96
|
async refreshToken() {
|
|
88
97
|
const data = await this.authPost("/refresh", {}, true);
|
|
89
98
|
const accessToken = extractAccessToken(data);
|
|
@@ -91,13 +100,15 @@ export class CemiarAuthClient {
|
|
|
91
100
|
return { accessToken };
|
|
92
101
|
}
|
|
93
102
|
async logout(options = {}) {
|
|
103
|
+
const loginMethod = this.getCurrentLoginMethod();
|
|
94
104
|
if (options.clearToken !== false) {
|
|
95
105
|
this.setAccessToken(null);
|
|
96
106
|
}
|
|
97
107
|
const redirectUrl = options.redirectUrl || this.logoutRedirectUrl;
|
|
108
|
+
const logoutPath = loginMethod === "microsoft" ? "/microsoft/logout" : "/logout";
|
|
98
109
|
const logoutUrl = redirectUrl
|
|
99
|
-
? `${this.baseUrl}
|
|
100
|
-
: `${this.baseUrl}
|
|
110
|
+
? `${this.baseUrl}${logoutPath}?redirectUrl=${encodeURIComponent(redirectUrl)}`
|
|
111
|
+
: `${this.baseUrl}${logoutPath}`;
|
|
101
112
|
try {
|
|
102
113
|
if (options.performRedirect === false || !isBrowser) {
|
|
103
114
|
await axios.post(`${this.baseUrl}/logout`, {}, { withCredentials: true });
|
|
@@ -165,6 +176,46 @@ export class CemiarAuthClient {
|
|
|
165
176
|
this.setAccessToken(null);
|
|
166
177
|
(_a = this.onAuthFailure) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
167
178
|
}
|
|
179
|
+
loadLoginMethodFromStorage() {
|
|
180
|
+
if (!isBrowser || !("localStorage" in globalThis)) {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
try {
|
|
184
|
+
const value = window.localStorage.getItem(LOGIN_METHOD_STORAGE_KEY);
|
|
185
|
+
if (value === "emailCode" || value === "microsoft") {
|
|
186
|
+
return value;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
console.warn("Failed to read login method from localStorage", error);
|
|
191
|
+
}
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
persistLoginMethod(method) {
|
|
195
|
+
this.loginMethod = method;
|
|
196
|
+
if (!isBrowser || !("localStorage" in globalThis)) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
try {
|
|
200
|
+
if (method) {
|
|
201
|
+
window.localStorage.setItem(LOGIN_METHOD_STORAGE_KEY, method);
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
window.localStorage.removeItem(LOGIN_METHOD_STORAGE_KEY);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
console.warn("Failed to persist login method in localStorage", error);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
getCurrentLoginMethod() {
|
|
212
|
+
if (this.loginMethod) {
|
|
213
|
+
return this.loginMethod;
|
|
214
|
+
}
|
|
215
|
+
const stored = this.loadLoginMethodFromStorage();
|
|
216
|
+
this.loginMethod = stored;
|
|
217
|
+
return stored;
|
|
218
|
+
}
|
|
168
219
|
}
|
|
169
220
|
export function createCemiarAuthClient(config) {
|
|
170
221
|
return new CemiarAuthClient(config);
|
package/dist/shared/Types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AxiosInstance, AxiosRequestConfig } from "axios";
|
|
2
2
|
import type { Request, ResponseToolkit } from "@hapi/hapi";
|
|
3
|
+
import { UserInfo } from "../browser/CemiarAuthClient.js";
|
|
3
4
|
export interface TokenStorage {
|
|
4
5
|
getToken(): string | null;
|
|
5
6
|
setToken(token: string | null): void;
|
|
@@ -71,9 +72,9 @@ export interface CemiarAuthClientInstance {
|
|
|
71
72
|
getAccessToken(): string | null;
|
|
72
73
|
setAccessToken(token: string | null): void;
|
|
73
74
|
getMicrosoftLoginUrl(params?: Record<string, string>): string;
|
|
75
|
+
handleRedirectMicrosoftCallback(routeQueryData: Record<string, string>): UserInfo | null;
|
|
74
76
|
sendEmailCode(email: string): Promise<EmailCodeResponse>;
|
|
75
|
-
verifyEmailCode(email: string, code: string): Promise<
|
|
76
|
-
exchangeMicrosoftCode(code: string): Promise<AuthTokens>;
|
|
77
|
+
verifyEmailCode(email: string, code: string): Promise<UserInfo | null>;
|
|
77
78
|
refreshToken(): Promise<AuthTokens>;
|
|
78
79
|
logout(options?: LogoutOptions): Promise<void>;
|
|
79
80
|
createApiClient(options: CreateApiClientOptions): AxiosInstance;
|
package/dist/shared/Utils.d.ts
CHANGED
package/dist/shared/Utils.js
CHANGED
|
@@ -4,4 +4,10 @@ export function normalizeBaseUrl(url) {
|
|
|
4
4
|
}
|
|
5
5
|
return url.endsWith("/") ? url.slice(0, -1) : url;
|
|
6
6
|
}
|
|
7
|
+
export function tokenToClaim(token) {
|
|
8
|
+
const payload = token.split(".")[1];
|
|
9
|
+
const decoded = Buffer.from(payload, "base64").toString("utf-8");
|
|
10
|
+
const parsed = JSON.parse(decoded);
|
|
11
|
+
return parsed;
|
|
12
|
+
}
|
|
7
13
|
export const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface Claim {
|
|
2
|
+
displayName: string;
|
|
3
|
+
sub: string;
|
|
4
|
+
iss: string;
|
|
5
|
+
raw: Record<string, any>;
|
|
6
|
+
actor_type: ActorType;
|
|
7
|
+
}
|
|
8
|
+
export declare enum ActorType {
|
|
9
|
+
INTERNAL_USER = "internal_user",
|
|
10
|
+
CUSTOMER_USER = "customer_user",
|
|
11
|
+
AUTOMATION_SERVICE = "automation_service",
|
|
12
|
+
ADMIN = "admin"
|
|
13
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export var ActorType;
|
|
2
|
+
(function (ActorType) {
|
|
3
|
+
ActorType["INTERNAL_USER"] = "internal_user";
|
|
4
|
+
ActorType["CUSTOMER_USER"] = "customer_user";
|
|
5
|
+
ActorType["AUTOMATION_SERVICE"] = "automation_service";
|
|
6
|
+
ActorType["ADMIN"] = "admin";
|
|
7
|
+
})(ActorType || (ActorType = {}));
|