@cemiar/auth-sdk 1.0.0 → 1.0.2

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.
@@ -1,5 +1,5 @@
1
1
  import { AxiosInstance } from "axios";
2
- import type { AuthTokens, CemiarAuthClientConfig, CemiarAuthClientInstance, CreateApiClientOptions, EmailCodeResponse, LogoutOptions, VerifyEmailResult } from "../shared/Types.js";
2
+ import { AuthTokens, CemiarAuthClientInstance, CemiarAuthClientConfig, EmailCodeResponse, LogoutOptions, CreateApiClientOptions, UserInfo } from "../shared/Types.js";
3
3
  export declare class CemiarAuthClient implements CemiarAuthClientInstance {
4
4
  private readonly baseUrl;
5
5
  private readonly tenantId;
@@ -9,15 +9,16 @@ export declare class CemiarAuthClient implements CemiarAuthClientInstance {
9
9
  private readonly onTokenChange?;
10
10
  private readonly onAuthFailure?;
11
11
  private readonly logoutRedirectUrl?;
12
+ private loginMethod;
12
13
  private refreshPromise;
13
14
  constructor(config: CemiarAuthClientConfig);
14
15
  getAccessToken(): string | null;
15
16
  setAccessToken(token: string | null): void;
16
17
  private authPost;
17
18
  sendEmailCode(email: string): Promise<EmailCodeResponse>;
18
- verifyEmailCode(email: string, code: string): Promise<VerifyEmailResult>;
19
+ verifyEmailCode(email: string, code: string): Promise<UserInfo | null>;
20
+ handleRedirectMicrosoftCallback(routeQueryData: Record<string, string>): UserInfo | null;
19
21
  getMicrosoftLoginUrl(extraParams?: Record<string, string>): string;
20
- exchangeMicrosoftCode(code: string): Promise<AuthTokens>;
21
22
  refreshToken(): Promise<AuthTokens>;
22
23
  logout(options?: LogoutOptions): Promise<void>;
23
24
  createApiClient(options: CreateApiClientOptions): AxiosInstance;
@@ -26,5 +27,8 @@ export declare class CemiarAuthClient implements CemiarAuthClientInstance {
26
27
  private handleResponseError;
27
28
  private queueTokenRefresh;
28
29
  private handleAuthFailure;
30
+ private loadLoginMethodFromStorage;
31
+ private persistLoginMethod;
32
+ private getCurrentLoginMethod;
29
33
  }
30
34
  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
- accessToken,
61
- authenticated: Boolean(data.authenticated),
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,18 @@ export class CemiarAuthClient {
91
100
  return { accessToken };
92
101
  }
93
102
  async logout(options = {}) {
94
- if (options.clearToken !== false) {
95
- this.setAccessToken(null);
103
+ let loginMethod = options.method;
104
+ if (!loginMethod) {
105
+ loginMethod = this.getCurrentLoginMethod();
106
+ if (options.clearToken !== false) {
107
+ this.setAccessToken(null);
108
+ }
96
109
  }
97
110
  const redirectUrl = options.redirectUrl || this.logoutRedirectUrl;
111
+ const logoutPath = loginMethod === "microsoft" ? "/microsoft/logout" : "/logout";
98
112
  const logoutUrl = redirectUrl
99
- ? `${this.baseUrl}/microsoft/logout?redirectUrl=${encodeURIComponent(redirectUrl)}`
100
- : `${this.baseUrl}/logout`;
113
+ ? `${this.baseUrl}${logoutPath}?redirectUrl=${encodeURIComponent(redirectUrl)}`
114
+ : `${this.baseUrl}${logoutPath}`;
101
115
  try {
102
116
  if (options.performRedirect === false || !isBrowser) {
103
117
  await axios.post(`${this.baseUrl}/logout`, {}, { withCredentials: true });
@@ -165,6 +179,46 @@ export class CemiarAuthClient {
165
179
  this.setAccessToken(null);
166
180
  (_a = this.onAuthFailure) === null || _a === void 0 ? void 0 : _a.call(this);
167
181
  }
182
+ loadLoginMethodFromStorage() {
183
+ if (!isBrowser || !("localStorage" in globalThis)) {
184
+ return null;
185
+ }
186
+ try {
187
+ const value = window.localStorage.getItem(LOGIN_METHOD_STORAGE_KEY);
188
+ if (value === "emailCode" || value === "microsoft") {
189
+ return value;
190
+ }
191
+ }
192
+ catch (error) {
193
+ console.warn("Failed to read login method from localStorage", error);
194
+ }
195
+ return null;
196
+ }
197
+ persistLoginMethod(method) {
198
+ this.loginMethod = method;
199
+ if (!isBrowser || !("localStorage" in globalThis)) {
200
+ return;
201
+ }
202
+ try {
203
+ if (method) {
204
+ window.localStorage.setItem(LOGIN_METHOD_STORAGE_KEY, method);
205
+ }
206
+ else {
207
+ window.localStorage.removeItem(LOGIN_METHOD_STORAGE_KEY);
208
+ }
209
+ }
210
+ catch (error) {
211
+ console.warn("Failed to persist login method in localStorage", error);
212
+ }
213
+ }
214
+ getCurrentLoginMethod() {
215
+ if (this.loginMethod) {
216
+ return this.loginMethod;
217
+ }
218
+ const stored = this.loadLoginMethodFromStorage();
219
+ this.loginMethod = stored;
220
+ return stored;
221
+ }
168
222
  }
169
223
  export function createCemiarAuthClient(config) {
170
224
  return new CemiarAuthClient(config);
@@ -1,5 +1,10 @@
1
1
  import type { AxiosInstance, AxiosRequestConfig } from "axios";
2
2
  import type { Request, ResponseToolkit } from "@hapi/hapi";
3
+ export type LoginMethod = "emailCode" | "microsoft";
4
+ export interface UserInfo {
5
+ email: string;
6
+ displayName?: string;
7
+ }
3
8
  export interface TokenStorage {
4
9
  getToken(): string | null;
5
10
  setToken(token: string | null): void;
@@ -42,6 +47,7 @@ export interface CreateApiClientOptions {
42
47
  axiosConfig?: AxiosRequestConfig;
43
48
  }
44
49
  export interface LogoutOptions {
50
+ method?: LoginMethod;
45
51
  redirectUrl?: string;
46
52
  performRedirect?: boolean;
47
53
  clearToken?: boolean;
@@ -71,9 +77,9 @@ export interface CemiarAuthClientInstance {
71
77
  getAccessToken(): string | null;
72
78
  setAccessToken(token: string | null): void;
73
79
  getMicrosoftLoginUrl(params?: Record<string, string>): string;
80
+ handleRedirectMicrosoftCallback(routeQueryData: Record<string, string>): UserInfo | null;
74
81
  sendEmailCode(email: string): Promise<EmailCodeResponse>;
75
- verifyEmailCode(email: string, code: string): Promise<VerifyEmailResult>;
76
- exchangeMicrosoftCode(code: string): Promise<AuthTokens>;
82
+ verifyEmailCode(email: string, code: string): Promise<UserInfo | null>;
77
83
  refreshToken(): Promise<AuthTokens>;
78
84
  logout(options?: LogoutOptions): Promise<void>;
79
85
  createApiClient(options: CreateApiClientOptions): AxiosInstance;
@@ -1,2 +1,4 @@
1
+ import { Claim } from "./models/Claim.js";
1
2
  export declare function normalizeBaseUrl(url: string): string;
3
+ export declare function tokenToClaim(token: string): Claim;
2
4
  export declare const isBrowser: boolean;
@@ -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 = {}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cemiar/auth-sdk",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Cemiar Auth integration helpers for web apps and APIs.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",