@civic/auth 0.6.0-beta.2 → 0.6.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.
- package/dist/shared/hooks/useSignIn.d.ts.map +1 -1
- package/dist/shared/hooks/useSignIn.js +8 -0
- package/dist/shared/hooks/useSignIn.js.map +1 -1
- package/dist/shared/version.d.ts +1 -1
- package/dist/shared/version.d.ts.map +1 -1
- package/dist/shared/version.js +1 -1
- package/dist/shared/version.js.map +1 -1
- package/dist/vanillajs/auth/AuthenticationEvents.d.ts +11 -0
- package/dist/vanillajs/auth/AuthenticationEvents.d.ts.map +1 -0
- package/dist/vanillajs/auth/AuthenticationEvents.js +36 -0
- package/dist/vanillajs/auth/AuthenticationEvents.js.map +1 -0
- package/dist/vanillajs/auth/CivicAuth.d.ts +147 -0
- package/dist/vanillajs/auth/CivicAuth.d.ts.map +1 -0
- package/dist/vanillajs/auth/CivicAuth.js +513 -0
- package/dist/vanillajs/auth/CivicAuth.js.map +1 -0
- package/dist/vanillajs/auth/SessionManager.d.ts +17 -0
- package/dist/vanillajs/auth/SessionManager.d.ts.map +1 -0
- package/dist/vanillajs/auth/SessionManager.js +86 -0
- package/dist/vanillajs/auth/SessionManager.js.map +1 -0
- package/dist/vanillajs/iframe/SignalObserver.d.ts +33 -0
- package/dist/vanillajs/iframe/SignalObserver.d.ts.map +1 -0
- package/dist/vanillajs/iframe/SignalObserver.js +162 -0
- package/dist/vanillajs/iframe/SignalObserver.js.map +1 -0
- package/dist/vanillajs/iframe/domUtils.d.ts +4 -0
- package/dist/vanillajs/iframe/domUtils.d.ts.map +1 -0
- package/dist/vanillajs/iframe/domUtils.js +25 -0
- package/dist/vanillajs/iframe/domUtils.js.map +1 -0
- package/dist/vanillajs/index.d.ts +16 -0
- package/dist/vanillajs/index.d.ts.map +1 -0
- package/dist/vanillajs/index.js +15 -0
- package/dist/vanillajs/index.js.map +1 -0
- package/dist/vanillajs/services/ApiService.d.ts +22 -0
- package/dist/vanillajs/services/ApiService.d.ts.map +1 -0
- package/dist/vanillajs/services/ApiService.js +82 -0
- package/dist/vanillajs/services/ApiService.js.map +1 -0
- package/dist/vanillajs/storage/BrowserCookieStorageAdapter.d.ts +19 -0
- package/dist/vanillajs/storage/BrowserCookieStorageAdapter.d.ts.map +1 -0
- package/dist/vanillajs/storage/BrowserCookieStorageAdapter.js +101 -0
- package/dist/vanillajs/storage/BrowserCookieStorageAdapter.js.map +1 -0
- package/dist/vanillajs/storage/BrowserLocalStorageAdapter.d.ts +9 -0
- package/dist/vanillajs/storage/BrowserLocalStorageAdapter.d.ts.map +1 -0
- package/dist/vanillajs/storage/BrowserLocalStorageAdapter.js +36 -0
- package/dist/vanillajs/storage/BrowserLocalStorageAdapter.js.map +1 -0
- package/dist/vanillajs/storage/InMemoryStorageAdapter.d.ts +9 -0
- package/dist/vanillajs/storage/InMemoryStorageAdapter.d.ts.map +1 -0
- package/dist/vanillajs/storage/InMemoryStorageAdapter.js +16 -0
- package/dist/vanillajs/storage/InMemoryStorageAdapter.js.map +1 -0
- package/dist/vanillajs/storage/StorageAdapter.d.ts +15 -0
- package/dist/vanillajs/storage/StorageAdapter.d.ts.map +1 -0
- package/dist/vanillajs/storage/StorageAdapter.js +16 -0
- package/dist/vanillajs/storage/StorageAdapter.js.map +1 -0
- package/dist/vanillajs/types/index.d.ts +28 -0
- package/dist/vanillajs/types/index.d.ts.map +1 -0
- package/dist/vanillajs/types/index.js +14 -0
- package/dist/vanillajs/types/index.js.map +1 -0
- package/dist/vanillajs/utils/auth-utils.d.ts +13 -0
- package/dist/vanillajs/utils/auth-utils.d.ts.map +1 -0
- package/dist/vanillajs/utils/auth-utils.js +15 -0
- package/dist/vanillajs/utils/auth-utils.js.map +1 -0
- package/dist/vanillajs/utils/logger.d.ts +29 -0
- package/dist/vanillajs/utils/logger.d.ts.map +1 -0
- package/dist/vanillajs/utils/logger.js +62 -0
- package/dist/vanillajs/utils/logger.js.map +1 -0
- package/dist/vanillajs/utils/page-handlers.d.ts +29 -0
- package/dist/vanillajs/utils/page-handlers.d.ts.map +1 -0
- package/dist/vanillajs/utils/page-handlers.js +165 -0
- package/dist/vanillajs/utils/page-handlers.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
import { createIframe, removeIframe } from "../iframe/domUtils.js";
|
|
2
|
+
import { AuthEvent } from "../types/index.js";
|
|
3
|
+
import { StorageAdapterToAuthStorage, } from "../storage/StorageAdapter.js";
|
|
4
|
+
import { BrowserCookieStorageAdapter } from "../storage/BrowserCookieStorageAdapter.js";
|
|
5
|
+
import { handleOAuthRedirectPage } from "../utils/page-handlers.js";
|
|
6
|
+
import { buildAuthUrl } from "../utils/auth-utils.js";
|
|
7
|
+
import { createLogger, configureLogging, setCurrentLogger, } from "../utils/logger.js";
|
|
8
|
+
import { SignalObserver } from "../iframe/SignalObserver.js";
|
|
9
|
+
import { GenericPublicClientPKCEProducer } from "../../services/PKCE.js";
|
|
10
|
+
import { generateState } from "../../lib/oauth.js";
|
|
11
|
+
/**
|
|
12
|
+
* Error codes for CivicAuth errors
|
|
13
|
+
*/
|
|
14
|
+
export var CivicAuthErrorCode;
|
|
15
|
+
(function (CivicAuthErrorCode) {
|
|
16
|
+
CivicAuthErrorCode["CONFIG_REQUIRED"] = "CONFIG_REQUIRED";
|
|
17
|
+
CivicAuthErrorCode["INIT_FAILED"] = "INIT_FAILED";
|
|
18
|
+
CivicAuthErrorCode["ENDPOINTS_NOT_INITIALIZED"] = "ENDPOINTS_NOT_INITIALIZED";
|
|
19
|
+
CivicAuthErrorCode["CONTAINER_NOT_FOUND"] = "CONTAINER_NOT_FOUND";
|
|
20
|
+
CivicAuthErrorCode["AUTH_PROCESS_TIMEOUT"] = "AUTH_PROCESS_TIMEOUT";
|
|
21
|
+
CivicAuthErrorCode["IFRAME_LOAD_ERROR"] = "IFRAME_LOAD_ERROR";
|
|
22
|
+
CivicAuthErrorCode["INVALID_MESSAGE"] = "INVALID_MESSAGE";
|
|
23
|
+
})(CivicAuthErrorCode || (CivicAuthErrorCode = {}));
|
|
24
|
+
/**
|
|
25
|
+
* Constants for the auth client
|
|
26
|
+
*/
|
|
27
|
+
const CONSTANTS = {
|
|
28
|
+
DEFAULT_IFRAME_ID: "civic-auth-iframe",
|
|
29
|
+
DEFAULT_AUTH_PROCESS_TIMEOUT: 60000, // 60 seconds
|
|
30
|
+
SUCCESS_SIGNAL_ID: "civic-auth-success-signal",
|
|
31
|
+
ERROR_SIGNAL_ID: "civic-auth-error-signal",
|
|
32
|
+
};
|
|
33
|
+
class CivicAuthError extends Error {
|
|
34
|
+
code;
|
|
35
|
+
constructor(message, code) {
|
|
36
|
+
super(message);
|
|
37
|
+
this.code = code;
|
|
38
|
+
this.name = "CivicAuthError";
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* CivicAuth client for handling OAuth authentication
|
|
43
|
+
*/
|
|
44
|
+
export class CivicAuth {
|
|
45
|
+
config;
|
|
46
|
+
iframeElement;
|
|
47
|
+
observer;
|
|
48
|
+
authPromise;
|
|
49
|
+
authPromiseResolve;
|
|
50
|
+
authPromiseReject;
|
|
51
|
+
authProcessTimeoutHandle;
|
|
52
|
+
messageEventHandler;
|
|
53
|
+
storage;
|
|
54
|
+
endpoints;
|
|
55
|
+
logger;
|
|
56
|
+
/**
|
|
57
|
+
* Private constructor for CivicAuth.
|
|
58
|
+
* Use {@link CivicAuth.create} to create a new instance.
|
|
59
|
+
* @param config - Configuration options for the auth client
|
|
60
|
+
* @throws {CivicAuthError} If required configuration is missing
|
|
61
|
+
* @private
|
|
62
|
+
*/
|
|
63
|
+
constructor(config) {
|
|
64
|
+
const loggingConfig = {
|
|
65
|
+
enabled: true,
|
|
66
|
+
namespace: "iframe",
|
|
67
|
+
level: "debug",
|
|
68
|
+
...config.logging,
|
|
69
|
+
};
|
|
70
|
+
this.config = {
|
|
71
|
+
displayMode: "iframe",
|
|
72
|
+
authProcessTimeout: config.authProcessTimeout || CONSTANTS.DEFAULT_AUTH_PROCESS_TIMEOUT,
|
|
73
|
+
iframeId: config.iframeId || CONSTANTS.DEFAULT_IFRAME_ID,
|
|
74
|
+
logging: loggingConfig,
|
|
75
|
+
...config,
|
|
76
|
+
};
|
|
77
|
+
// Configure logging based on config
|
|
78
|
+
configureLogging(loggingConfig);
|
|
79
|
+
// Initialize logger based on config
|
|
80
|
+
if (this.config.logging?.enabled) {
|
|
81
|
+
const namespace = this.config.logging.namespace || "iframe";
|
|
82
|
+
this.logger = createLogger(namespace);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
// Create a no-op logger when logging is disabled
|
|
86
|
+
this.logger = {
|
|
87
|
+
debug: () => { },
|
|
88
|
+
info: () => { },
|
|
89
|
+
warn: () => { },
|
|
90
|
+
error: () => { },
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
// Set this logger as the current logger
|
|
94
|
+
setCurrentLogger(this.logger);
|
|
95
|
+
this.storage = config.storageAdapter || new BrowserCookieStorageAdapter();
|
|
96
|
+
// Validate required configuration
|
|
97
|
+
const requiredConfigs = [
|
|
98
|
+
{ key: "clientId", value: config.clientId },
|
|
99
|
+
{ key: "targetContainerElement", value: config.targetContainerElement },
|
|
100
|
+
{ key: "textSignals.success", value: config.textSignals?.success },
|
|
101
|
+
];
|
|
102
|
+
for (const { key, value } of requiredConfigs) {
|
|
103
|
+
if (!value) {
|
|
104
|
+
throw new CivicAuthError(`CivicAuth: ${key} is required.`, CivicAuthErrorCode.CONFIG_REQUIRED);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
this.messageEventHandler = this.handleIframeMessage.bind(this);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Creates and initializes a new instance of CivicAuth.
|
|
111
|
+
* This is the recommended way to create a CivicAuth instance.
|
|
112
|
+
*
|
|
113
|
+
* @param config - Configuration options for the auth client
|
|
114
|
+
* @returns A promise that resolves with the initialized CivicAuth instance
|
|
115
|
+
* @throws {CivicAuthError} If initialization fails or required configuration is missing
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```typescript
|
|
119
|
+
* const auth = await CivicAuth.create({
|
|
120
|
+
* clientId: "your-client-id",
|
|
121
|
+
* redirectUrl: "https://your-app.com/callback",
|
|
122
|
+
* oauthServerBaseUrl: "https://auth-server.com/",
|
|
123
|
+
* scopes: ["openid", "profile"],
|
|
124
|
+
* targetContainerElement: "auth-container",
|
|
125
|
+
* textSignals: {
|
|
126
|
+
* success: "Authentication successful!"
|
|
127
|
+
* }
|
|
128
|
+
* });
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
static async create(config) {
|
|
132
|
+
const instance = new CivicAuth(config);
|
|
133
|
+
await instance.init();
|
|
134
|
+
return instance;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Initializes the auth client and checks for callback handling
|
|
138
|
+
* @throws {CivicAuthError} If initialization fails
|
|
139
|
+
*/
|
|
140
|
+
async init() {
|
|
141
|
+
try {
|
|
142
|
+
// Get OAuth endpoints from well-known configuration
|
|
143
|
+
this.endpoints = {
|
|
144
|
+
auth: `${this.config.oauthServerBaseUrl}auth`,
|
|
145
|
+
token: `${this.config.oauthServerBaseUrl}token`,
|
|
146
|
+
jwks: `${this.config.oauthServerBaseUrl}jwks`,
|
|
147
|
+
userinfo: `${this.config.oauthServerBaseUrl}userinfo`,
|
|
148
|
+
endsession: `${this.config.oauthServerBaseUrl}endsession`,
|
|
149
|
+
};
|
|
150
|
+
// Check if we're on the callback page
|
|
151
|
+
if (window.location.href.startsWith(this.config.redirectUrl)) {
|
|
152
|
+
await this.handleCallback();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
const errorMessage = error instanceof Error
|
|
157
|
+
? error.message
|
|
158
|
+
: "Failed to initialize authentication";
|
|
159
|
+
this.logger.error("Failed to initialize CivicAuth:", { error });
|
|
160
|
+
this.config.events?.emit(AuthEvent.SIGN_IN_ERROR, {
|
|
161
|
+
detail: errorMessage,
|
|
162
|
+
});
|
|
163
|
+
throw new CivicAuthError(errorMessage, CivicAuthErrorCode.INIT_FAILED);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Gets the container element for the auth iframe
|
|
168
|
+
* @returns The container element or null if not found
|
|
169
|
+
*/
|
|
170
|
+
getContainerElement() {
|
|
171
|
+
if (typeof this.config.targetContainerElement === "string") {
|
|
172
|
+
const element = document.getElementById(this.config.targetContainerElement);
|
|
173
|
+
if (!element) {
|
|
174
|
+
this.logger.warn(`Container element with ID "${this.config.targetContainerElement}" not found`);
|
|
175
|
+
}
|
|
176
|
+
return element;
|
|
177
|
+
}
|
|
178
|
+
return this.config.targetContainerElement;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Builds the authentication URL with PKCE challenge
|
|
182
|
+
* @returns The complete authentication URL
|
|
183
|
+
* @throws {CivicAuthError} If endpoints are not initialized
|
|
184
|
+
*/
|
|
185
|
+
async buildAuthUrl() {
|
|
186
|
+
if (!this.endpoints) {
|
|
187
|
+
throw new CivicAuthError("OAuth endpoints not initialized. Please wait for initialization to complete.", CivicAuthErrorCode.ENDPOINTS_NOT_INITIALIZED);
|
|
188
|
+
}
|
|
189
|
+
const authStorage = new StorageAdapterToAuthStorage(this.storage);
|
|
190
|
+
const pkceProducer = new GenericPublicClientPKCEProducer(authStorage);
|
|
191
|
+
const codeChallenge = await pkceProducer.getCodeChallenge();
|
|
192
|
+
const state = this.config.initialState ||
|
|
193
|
+
generateState({
|
|
194
|
+
displayMode: this.config.displayMode || "iframe",
|
|
195
|
+
});
|
|
196
|
+
return buildAuthUrl({
|
|
197
|
+
endpoints: this.endpoints,
|
|
198
|
+
clientId: this.config.clientId,
|
|
199
|
+
redirectUrl: this.config.redirectUrl,
|
|
200
|
+
scopes: this.config.scopes,
|
|
201
|
+
codeChallenge,
|
|
202
|
+
state,
|
|
203
|
+
prompt: this.config.prompt,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
handleIframeMessage(event) {
|
|
207
|
+
const expectedOrigin = new URL(this.config.oauthServerBaseUrl).origin;
|
|
208
|
+
this.logIncomingMessage(event, expectedOrigin);
|
|
209
|
+
if (!this.isValidMessageSource(event, expectedOrigin)) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
this.handleValidMessage(event);
|
|
213
|
+
}
|
|
214
|
+
logIncomingMessage(event, expectedOrigin) {
|
|
215
|
+
this.logger.debug("Global window received message:", {
|
|
216
|
+
data: event.data,
|
|
217
|
+
origin: event.origin,
|
|
218
|
+
sourceProvided: !!event.source,
|
|
219
|
+
iframeContentWindow: this.iframeElement?.contentWindow,
|
|
220
|
+
expectedIframeOrigin: expectedOrigin,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
isValidMessageSource(event, expectedOrigin) {
|
|
224
|
+
const isValidOrigin = event.origin === expectedOrigin;
|
|
225
|
+
const isValidSource = event.source === this.iframeElement?.contentWindow;
|
|
226
|
+
if (!isValidOrigin) {
|
|
227
|
+
this.logger.warn("Ignored message from unexpected origin.", {
|
|
228
|
+
receivedOrigin: event.origin,
|
|
229
|
+
expectedOrigin,
|
|
230
|
+
iframeSrc: this.iframeElement?.src,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
if (!isValidSource) {
|
|
234
|
+
this.logger.warn("Ignored message from unexpected source.", {
|
|
235
|
+
isSourceProvided: !!event.source,
|
|
236
|
+
isIframeContentWindowAvailable: !!this.iframeElement?.contentWindow,
|
|
237
|
+
iframeSrc: this.iframeElement?.src,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
return isValidOrigin && isValidSource;
|
|
241
|
+
}
|
|
242
|
+
handleValidMessage(event) {
|
|
243
|
+
this.logger.info("Message from configured iframe source and origin received", {
|
|
244
|
+
data: event.data,
|
|
245
|
+
iframeSrc: this.iframeElement?.src,
|
|
246
|
+
});
|
|
247
|
+
const message = event.data;
|
|
248
|
+
const messageType = message?.type;
|
|
249
|
+
switch (messageType) {
|
|
250
|
+
case "auth_success":
|
|
251
|
+
this.handleAuthSuccess(message);
|
|
252
|
+
break;
|
|
253
|
+
case "auth_error":
|
|
254
|
+
this.handleAuthError(message);
|
|
255
|
+
break;
|
|
256
|
+
default:
|
|
257
|
+
this.logger.debug("Message from iframe did not match expected types (auth_success, auth_error)", { data: event.data });
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
handleAuthSuccess(data) {
|
|
261
|
+
this.config.events?.emit(AuthEvent.SIGN_IN_COMPLETE, {
|
|
262
|
+
detail: "Success signal received via postMessage",
|
|
263
|
+
data,
|
|
264
|
+
});
|
|
265
|
+
this.authPromiseResolve?.(data?.data || {});
|
|
266
|
+
this.cleanup();
|
|
267
|
+
}
|
|
268
|
+
handleAuthError(data) {
|
|
269
|
+
this.config.events?.emit(AuthEvent.SIGN_IN_ERROR, {
|
|
270
|
+
detail: "Error signal received via postMessage",
|
|
271
|
+
error: data,
|
|
272
|
+
});
|
|
273
|
+
this.authPromiseReject?.(new CivicAuthError(data?.detail || "Error signal received via postMessage", CivicAuthErrorCode.INVALID_MESSAGE));
|
|
274
|
+
this.cleanup();
|
|
275
|
+
}
|
|
276
|
+
setupSignalObserver(iframeDoc) {
|
|
277
|
+
const signalObserver = new SignalObserver({
|
|
278
|
+
textSignals: this.config.textSignals,
|
|
279
|
+
events: this.config.events,
|
|
280
|
+
logger: this.logger,
|
|
281
|
+
}, this.authPromiseResolve, this.authPromiseReject, () => this.cleanup());
|
|
282
|
+
signalObserver.setup(iframeDoc);
|
|
283
|
+
}
|
|
284
|
+
async handleNewTabAuth(fullAuthUrl, reject) {
|
|
285
|
+
const popupWindow = window.open(fullAuthUrl, "_blank");
|
|
286
|
+
if (!popupWindow) {
|
|
287
|
+
const error = new CivicAuthError("Failed to open popup window. Please check your browser's popup settings.", CivicAuthErrorCode.INIT_FAILED);
|
|
288
|
+
this.config.events?.emit(AuthEvent.SIGN_IN_ERROR, {
|
|
289
|
+
detail: error.message,
|
|
290
|
+
});
|
|
291
|
+
reject(error);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
async handleIframeAuth(fullAuthUrl, reject) {
|
|
295
|
+
const container = this.getContainerElement();
|
|
296
|
+
if (!container) {
|
|
297
|
+
const error = new CivicAuthError("Target container element not found.", CivicAuthErrorCode.CONTAINER_NOT_FOUND);
|
|
298
|
+
this.logger.error(error.message);
|
|
299
|
+
reject(error);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
this.logger.debug("Creating iframe", {
|
|
303
|
+
url: fullAuthUrl,
|
|
304
|
+
containerId: container?.id,
|
|
305
|
+
iframeId: this.config.iframeId,
|
|
306
|
+
origin: window.location.origin,
|
|
307
|
+
});
|
|
308
|
+
this.iframeElement = createIframe(fullAuthUrl, container, this.config.iframeId);
|
|
309
|
+
this.config.events?.emit(AuthEvent.SIGN_IN_STARTED, {
|
|
310
|
+
detail: "Iframe created",
|
|
311
|
+
});
|
|
312
|
+
this.iframeElement.onload = () => {
|
|
313
|
+
this.logger.info("Iframe loaded", {
|
|
314
|
+
iframeSrc: this.iframeElement?.src,
|
|
315
|
+
currentOrigin: window.location.origin,
|
|
316
|
+
expectedAuthServerOrigin: new URL(this.config.oauthServerBaseUrl)
|
|
317
|
+
.origin,
|
|
318
|
+
});
|
|
319
|
+
if (!this.iframeElement?.contentWindow) {
|
|
320
|
+
const errorMsg = "Iframe content window not available after load.";
|
|
321
|
+
this.logger.error(errorMsg, {
|
|
322
|
+
iframeSrc: this.iframeElement?.src,
|
|
323
|
+
});
|
|
324
|
+
reject(new Error(errorMsg));
|
|
325
|
+
this.cleanup();
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
// Set up postMessage listener for cross-origin communication
|
|
329
|
+
if (this.messageEventHandler) {
|
|
330
|
+
window.addEventListener("message", this.messageEventHandler);
|
|
331
|
+
this.logger.info("Added cross-origin message event listener for auth server communication", {
|
|
332
|
+
parentOrigin: window.location.origin,
|
|
333
|
+
authServerOrigin: new URL(this.config.oauthServerBaseUrl).origin,
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
this.logger.error("messageEventHandler was not defined when trying to add listener.");
|
|
338
|
+
}
|
|
339
|
+
// Try to detect redirect to our domain
|
|
340
|
+
try {
|
|
341
|
+
const currentIframeHref = this.iframeElement.contentWindow.location.href;
|
|
342
|
+
if (currentIframeHref.startsWith(this.config.redirectUrl)) {
|
|
343
|
+
this.logger.info("Iframe has navigated to redirectUrl (same-origin). Setting up DOM observer.");
|
|
344
|
+
if (this.iframeElement.contentDocument &&
|
|
345
|
+
this.iframeElement.contentDocument.body) {
|
|
346
|
+
this.setupSignalObserver(this.iframeElement.contentDocument);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
catch (error) {
|
|
351
|
+
this.logger.error("Error checking iframe href", {
|
|
352
|
+
error,
|
|
353
|
+
iframeSrc: this.iframeElement?.src,
|
|
354
|
+
});
|
|
355
|
+
// This is expected when the iframe is on the auth server domain
|
|
356
|
+
this.logger.info("Iframe is on auth server domain - using postMessage for communication", {
|
|
357
|
+
parentOrigin: window.location.origin,
|
|
358
|
+
authServerOrigin: new URL(this.config.oauthServerBaseUrl).origin,
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
this.iframeElement.onerror = (event) => {
|
|
363
|
+
this.logger.error("Iframe load error", {
|
|
364
|
+
event,
|
|
365
|
+
iframeSrc: this.iframeElement?.src,
|
|
366
|
+
currentOrigin: window.location.origin,
|
|
367
|
+
});
|
|
368
|
+
this.config.events?.emit(AuthEvent.SIGN_IN_ERROR, {
|
|
369
|
+
detail: "Iframe load error",
|
|
370
|
+
error: event,
|
|
371
|
+
});
|
|
372
|
+
reject(new Error("Iframe failed to load."));
|
|
373
|
+
this.cleanup();
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Starts the authentication process
|
|
378
|
+
* @returns A promise that resolves with the authentication result
|
|
379
|
+
* @throws {CivicAuthError} If authentication fails or times out
|
|
380
|
+
*/
|
|
381
|
+
async startAuthentication() {
|
|
382
|
+
if (!this.endpoints) {
|
|
383
|
+
const error = new CivicAuthError("OAuth endpoints not initialized. Please wait for initialization to complete.", CivicAuthErrorCode.ENDPOINTS_NOT_INITIALIZED);
|
|
384
|
+
this.config.events?.emit(AuthEvent.SIGN_IN_ERROR, {
|
|
385
|
+
detail: error.message,
|
|
386
|
+
});
|
|
387
|
+
throw error;
|
|
388
|
+
}
|
|
389
|
+
if (this.authPromise) {
|
|
390
|
+
this.logger.info("Authentication already in progress, returning existing promise");
|
|
391
|
+
return this.authPromise;
|
|
392
|
+
}
|
|
393
|
+
const fullAuthUrl = await this.buildAuthUrl();
|
|
394
|
+
this.logger.info("Starting authentication process", {
|
|
395
|
+
constructedIframeUrl: fullAuthUrl,
|
|
396
|
+
displayMode: this.config.displayMode,
|
|
397
|
+
authProcessTimeout: this.config.authProcessTimeout,
|
|
398
|
+
});
|
|
399
|
+
this.authPromise = new Promise((resolve, reject) => {
|
|
400
|
+
this.authPromiseResolve = resolve;
|
|
401
|
+
this.authPromiseReject = reject;
|
|
402
|
+
// Handle different display modes
|
|
403
|
+
switch (this.config.displayMode) {
|
|
404
|
+
case "redirect":
|
|
405
|
+
// For redirect mode, just navigate to the auth URL
|
|
406
|
+
window.location.href = fullAuthUrl;
|
|
407
|
+
break;
|
|
408
|
+
case "new_tab":
|
|
409
|
+
this.handleNewTabAuth(fullAuthUrl, reject);
|
|
410
|
+
break;
|
|
411
|
+
case "iframe":
|
|
412
|
+
default:
|
|
413
|
+
this.handleIframeAuth(fullAuthUrl, reject);
|
|
414
|
+
break;
|
|
415
|
+
}
|
|
416
|
+
// Set up timeout for all display modes
|
|
417
|
+
if (this.config.authProcessTimeout &&
|
|
418
|
+
this.config.authProcessTimeout > 0) {
|
|
419
|
+
this.logger.debug("Setting up authentication timeout", {
|
|
420
|
+
authProcessTimeout: this.config.authProcessTimeout,
|
|
421
|
+
displayMode: this.config.displayMode,
|
|
422
|
+
});
|
|
423
|
+
this.authProcessTimeoutHandle = window.setTimeout(() => {
|
|
424
|
+
this.logger.warn("Authentication timed out", {
|
|
425
|
+
displayMode: this.config.displayMode,
|
|
426
|
+
currentOrigin: window.location.origin,
|
|
427
|
+
});
|
|
428
|
+
this.config.events?.emit(AuthEvent.SIGN_IN_ERROR, {
|
|
429
|
+
detail: "Authentication timed out",
|
|
430
|
+
});
|
|
431
|
+
reject(new Error("Authentication timed out."));
|
|
432
|
+
this.cleanup();
|
|
433
|
+
}, this.config.authProcessTimeout);
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
return this.authPromise;
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Cleans up resources and event listeners
|
|
440
|
+
*/
|
|
441
|
+
cleanup() {
|
|
442
|
+
this.logger.info("Cleaning up iframe authentication client");
|
|
443
|
+
if (this.observer) {
|
|
444
|
+
this.logger.debug("Disconnecting mutation observer");
|
|
445
|
+
this.observer.disconnect();
|
|
446
|
+
this.observer = undefined;
|
|
447
|
+
}
|
|
448
|
+
if (this.messageEventHandler) {
|
|
449
|
+
window.removeEventListener("message", this.messageEventHandler);
|
|
450
|
+
this.logger.debug("Removed 'message' event listener from window.");
|
|
451
|
+
}
|
|
452
|
+
if (this.iframeElement) {
|
|
453
|
+
this.logger.debug("Removing iframe element");
|
|
454
|
+
removeIframe(this.iframeElement);
|
|
455
|
+
this.iframeElement = undefined;
|
|
456
|
+
}
|
|
457
|
+
if (this.authProcessTimeoutHandle) {
|
|
458
|
+
this.logger.debug("Clearing authentication process timeout");
|
|
459
|
+
window.clearTimeout(this.authProcessTimeoutHandle);
|
|
460
|
+
this.authProcessTimeoutHandle = undefined;
|
|
461
|
+
}
|
|
462
|
+
this.logger.debug("Resetting promise state");
|
|
463
|
+
this.authPromise = undefined;
|
|
464
|
+
this.authPromiseResolve = undefined;
|
|
465
|
+
this.authPromiseReject = undefined;
|
|
466
|
+
}
|
|
467
|
+
async handleCallback() {
|
|
468
|
+
try {
|
|
469
|
+
const callbackHandled = await handleOAuthRedirectPage({
|
|
470
|
+
clientId: this.config.clientId,
|
|
471
|
+
oauthServer: this.config.oauthServerBaseUrl,
|
|
472
|
+
redirectUrl: this.config.redirectUrl,
|
|
473
|
+
textSignals: {
|
|
474
|
+
success: this.config.textSignals.success,
|
|
475
|
+
error: this.config.textSignals.error || "Authentication failed",
|
|
476
|
+
},
|
|
477
|
+
});
|
|
478
|
+
if (callbackHandled) {
|
|
479
|
+
const successSignal = document.getElementById(CONSTANTS.SUCCESS_SIGNAL_ID);
|
|
480
|
+
const errorSignal = document.getElementById(CONSTANTS.ERROR_SIGNAL_ID);
|
|
481
|
+
if (successSignal) {
|
|
482
|
+
let userInfo = null;
|
|
483
|
+
const userInfoAttr = successSignal.getAttribute("data-user-info");
|
|
484
|
+
if (userInfoAttr) {
|
|
485
|
+
try {
|
|
486
|
+
userInfo = JSON.parse(userInfoAttr);
|
|
487
|
+
}
|
|
488
|
+
catch (error) {
|
|
489
|
+
this.logger.error("Failed to parse user info:", { error });
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
this.config.events?.emit(AuthEvent.SIGN_IN_COMPLETE, {
|
|
493
|
+
detail: "Callback processed successfully",
|
|
494
|
+
user: userInfo,
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
else if (errorSignal) {
|
|
498
|
+
this.config.events?.emit(AuthEvent.SIGN_IN_ERROR, {
|
|
499
|
+
detail: errorSignal.textContent || "Unknown error during callback",
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
catch (error) {
|
|
505
|
+
this.config.events?.emit(AuthEvent.SIGN_IN_ERROR, {
|
|
506
|
+
detail: error instanceof Error
|
|
507
|
+
? error.message
|
|
508
|
+
: "Unknown error during callback",
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
//# sourceMappingURL=CivicAuth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CivicAuth.js","sourceRoot":"","sources":["../../../src/vanillajs/auth/CivicAuth.ts"],"names":[],"mappings":"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","sourcesContent":["import { createIframe, removeIframe } from \"../iframe/domUtils.js\";\nimport { AuthEvent } from \"../types/index.js\";\nimport type { AuthenticationEvents } from \"./AuthenticationEvents.js\";\nimport {\n StorageAdapterToAuthStorage,\n type StorageAdapter,\n} from \"../storage/StorageAdapter.js\";\nimport { BrowserCookieStorageAdapter } from \"../storage/BrowserCookieStorageAdapter.js\";\nimport { handleOAuthRedirectPage } from \"../utils/page-handlers.js\";\nimport type { Endpoints, DisplayMode } from \"../../types.js\";\nimport { buildAuthUrl } from \"../utils/auth-utils.js\";\nimport type { AuthResult } from \"../types/index.js\";\nimport {\n createLogger,\n configureLogging,\n setCurrentLogger,\n} from \"../utils/logger.js\";\nimport { SignalObserver } from \"../iframe/SignalObserver.js\";\nimport { GenericPublicClientPKCEProducer } from \"../../services/PKCE.js\";\nimport { generateState } from \"../../lib/oauth.js\";\n\n/**\n * Error codes for CivicAuth errors\n */\nexport enum CivicAuthErrorCode {\n CONFIG_REQUIRED = \"CONFIG_REQUIRED\",\n INIT_FAILED = \"INIT_FAILED\",\n ENDPOINTS_NOT_INITIALIZED = \"ENDPOINTS_NOT_INITIALIZED\",\n CONTAINER_NOT_FOUND = \"CONTAINER_NOT_FOUND\",\n AUTH_PROCESS_TIMEOUT = \"AUTH_PROCESS_TIMEOUT\",\n IFRAME_LOAD_ERROR = \"IFRAME_LOAD_ERROR\",\n INVALID_MESSAGE = \"INVALID_MESSAGE\",\n}\n\n/**\n * Constants for the auth client\n */\nconst CONSTANTS = {\n DEFAULT_IFRAME_ID: \"civic-auth-iframe\",\n DEFAULT_AUTH_PROCESS_TIMEOUT: 60000, // 60 seconds\n SUCCESS_SIGNAL_ID: \"civic-auth-success-signal\",\n ERROR_SIGNAL_ID: \"civic-auth-error-signal\",\n} as const;\n\n/**\n * Message types for postMessage communication\n */\ntype AuthMessageType = \"auth_success\" | \"auth_error\";\n\ninterface AuthMessage {\n type: AuthMessageType;\n detail?: string;\n data?: unknown;\n error?: unknown;\n}\n\nclass CivicAuthError extends Error {\n constructor(\n message: string,\n public readonly code: CivicAuthErrorCode,\n ) {\n super(message);\n this.name = \"CivicAuthError\";\n }\n}\n\n/**\n * Configuration options for the CivicAuth client\n */\nexport interface CivicAuthClientConfig {\n /** OAuth client ID */\n clientId: string;\n /** URL to redirect to after authentication */\n redirectUrl: string;\n /** Base URL of the OAuth server */\n oauthServerBaseUrl: string;\n /** Array of OAuth scopes to request */\n scopes: string[];\n /** HTML element or element ID where the auth iframe will be mounted */\n targetContainerElement: HTMLElement | string;\n /** Text signals for success and error states */\n textSignals: {\n /** Text to display on successful authentication */\n success: string;\n /** Optional text to display on authentication error */\n error?: string;\n };\n /** Display mode for the authentication UI */\n displayMode?: DisplayMode;\n /**\n * Timeout duration in milliseconds for the entire authentication process.\n * If the authentication process takes longer than this duration, it will be cancelled\n * and an error will be thrown.\n */\n authProcessTimeout?: number;\n /** Event handlers for authentication events */\n events?: AuthenticationEvents;\n /** Custom ID for the auth iframe */\n iframeId?: string;\n /** Custom storage adapter for auth state */\n storageAdapter?: StorageAdapter;\n /** OAuth prompt parameter */\n prompt?: string;\n /** Initial state for OAuth flow */\n initialState?: string;\n /** Logging configuration */\n logging?: LoggingConfig;\n}\n\nexport interface LoggingConfig {\n enabled: boolean;\n namespace?: string;\n level?: \"debug\" | \"info\" | \"warn\" | \"error\";\n}\n\n/**\n * CivicAuth client for handling OAuth authentication\n */\nexport class CivicAuth {\n private config: CivicAuthClientConfig;\n private iframeElement?: HTMLIFrameElement;\n private observer?: MutationObserver;\n private authPromise?: Promise<AuthResult>;\n private authPromiseResolve?: (value: AuthResult) => void;\n private authPromiseReject?: (reason?: Error) => void;\n private authProcessTimeoutHandle?: number;\n private messageEventHandler?: (event: MessageEvent) => void;\n private storage: StorageAdapter;\n private endpoints?: Endpoints;\n private logger: ReturnType<typeof createLogger>;\n\n /**\n * Private constructor for CivicAuth.\n * Use {@link CivicAuth.create} to create a new instance.\n * @param config - Configuration options for the auth client\n * @throws {CivicAuthError} If required configuration is missing\n * @private\n */\n private constructor(config: CivicAuthClientConfig) {\n const loggingConfig = {\n enabled: true,\n namespace: \"iframe\",\n level: \"debug\" as const,\n ...config.logging,\n };\n\n this.config = {\n displayMode: \"iframe\",\n authProcessTimeout:\n config.authProcessTimeout || CONSTANTS.DEFAULT_AUTH_PROCESS_TIMEOUT,\n iframeId: config.iframeId || CONSTANTS.DEFAULT_IFRAME_ID,\n logging: loggingConfig,\n ...config,\n };\n\n // Configure logging based on config\n configureLogging(loggingConfig);\n\n // Initialize logger based on config\n if (this.config.logging?.enabled) {\n const namespace = this.config.logging.namespace || \"iframe\";\n this.logger = createLogger(namespace);\n } else {\n // Create a no-op logger when logging is disabled\n this.logger = {\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n };\n }\n\n // Set this logger as the current logger\n setCurrentLogger(this.logger);\n\n this.storage = config.storageAdapter || new BrowserCookieStorageAdapter();\n\n // Validate required configuration\n const requiredConfigs = [\n { key: \"clientId\", value: config.clientId },\n { key: \"targetContainerElement\", value: config.targetContainerElement },\n { key: \"textSignals.success\", value: config.textSignals?.success },\n ];\n\n for (const { key, value } of requiredConfigs) {\n if (!value) {\n throw new CivicAuthError(\n `CivicAuth: ${key} is required.`,\n CivicAuthErrorCode.CONFIG_REQUIRED,\n );\n }\n }\n\n this.messageEventHandler = this.handleIframeMessage.bind(this);\n }\n\n /**\n * Creates and initializes a new instance of CivicAuth.\n * This is the recommended way to create a CivicAuth instance.\n *\n * @param config - Configuration options for the auth client\n * @returns A promise that resolves with the initialized CivicAuth instance\n * @throws {CivicAuthError} If initialization fails or required configuration is missing\n *\n * @example\n * ```typescript\n * const auth = await CivicAuth.create({\n * clientId: \"your-client-id\",\n * redirectUrl: \"https://your-app.com/callback\",\n * oauthServerBaseUrl: \"https://auth-server.com/\",\n * scopes: [\"openid\", \"profile\"],\n * targetContainerElement: \"auth-container\",\n * textSignals: {\n * success: \"Authentication successful!\"\n * }\n * });\n * ```\n */\n public static async create(\n config: CivicAuthClientConfig,\n ): Promise<CivicAuth> {\n const instance = new CivicAuth(config);\n await instance.init();\n return instance;\n }\n\n /**\n * Initializes the auth client and checks for callback handling\n * @throws {CivicAuthError} If initialization fails\n */\n private async init(): Promise<void> {\n try {\n // Get OAuth endpoints from well-known configuration\n this.endpoints = {\n auth: `${this.config.oauthServerBaseUrl}auth`,\n token: `${this.config.oauthServerBaseUrl}token`,\n jwks: `${this.config.oauthServerBaseUrl}jwks`,\n userinfo: `${this.config.oauthServerBaseUrl}userinfo`,\n endsession: `${this.config.oauthServerBaseUrl}endsession`,\n };\n\n // Check if we're on the callback page\n if (window.location.href.startsWith(this.config.redirectUrl)) {\n await this.handleCallback();\n }\n } catch (error) {\n const errorMessage =\n error instanceof Error\n ? error.message\n : \"Failed to initialize authentication\";\n this.logger.error(\"Failed to initialize CivicAuth:\", { error });\n this.config.events?.emit(AuthEvent.SIGN_IN_ERROR, {\n detail: errorMessage,\n });\n throw new CivicAuthError(errorMessage, CivicAuthErrorCode.INIT_FAILED);\n }\n }\n\n /**\n * Gets the container element for the auth iframe\n * @returns The container element or null if not found\n */\n private getContainerElement(): HTMLElement | null {\n if (typeof this.config.targetContainerElement === \"string\") {\n const element = document.getElementById(\n this.config.targetContainerElement,\n );\n if (!element) {\n this.logger.warn(\n `Container element with ID \"${this.config.targetContainerElement}\" not found`,\n );\n }\n return element;\n }\n return this.config.targetContainerElement;\n }\n\n /**\n * Builds the authentication URL with PKCE challenge\n * @returns The complete authentication URL\n * @throws {CivicAuthError} If endpoints are not initialized\n */\n private async buildAuthUrl(): Promise<string> {\n if (!this.endpoints) {\n throw new CivicAuthError(\n \"OAuth endpoints not initialized. Please wait for initialization to complete.\",\n CivicAuthErrorCode.ENDPOINTS_NOT_INITIALIZED,\n );\n }\n\n const authStorage = new StorageAdapterToAuthStorage(this.storage);\n const pkceProducer = new GenericPublicClientPKCEProducer(authStorage);\n const codeChallenge = await pkceProducer.getCodeChallenge();\n const state =\n this.config.initialState ||\n generateState({\n displayMode: this.config.displayMode || \"iframe\",\n });\n\n return buildAuthUrl({\n endpoints: this.endpoints,\n clientId: this.config.clientId,\n redirectUrl: this.config.redirectUrl,\n scopes: this.config.scopes,\n codeChallenge,\n state,\n prompt: this.config.prompt,\n });\n }\n\n private handleIframeMessage(event: MessageEvent): void {\n const expectedOrigin = new URL(this.config.oauthServerBaseUrl).origin;\n this.logIncomingMessage(event, expectedOrigin);\n\n if (!this.isValidMessageSource(event, expectedOrigin)) {\n return;\n }\n\n this.handleValidMessage(event);\n }\n\n private logIncomingMessage(\n event: MessageEvent,\n expectedOrigin: string,\n ): void {\n this.logger.debug(\"Global window received message:\", {\n data: event.data,\n origin: event.origin,\n sourceProvided: !!event.source,\n iframeContentWindow: this.iframeElement?.contentWindow,\n expectedIframeOrigin: expectedOrigin,\n });\n }\n\n private isValidMessageSource(\n event: MessageEvent,\n expectedOrigin: string,\n ): boolean {\n const isValidOrigin = event.origin === expectedOrigin;\n const isValidSource = event.source === this.iframeElement?.contentWindow;\n\n if (!isValidOrigin) {\n this.logger.warn(\"Ignored message from unexpected origin.\", {\n receivedOrigin: event.origin,\n expectedOrigin,\n iframeSrc: this.iframeElement?.src,\n });\n }\n\n if (!isValidSource) {\n this.logger.warn(\"Ignored message from unexpected source.\", {\n isSourceProvided: !!event.source,\n isIframeContentWindowAvailable: !!this.iframeElement?.contentWindow,\n iframeSrc: this.iframeElement?.src,\n });\n }\n\n return isValidOrigin && isValidSource;\n }\n\n private handleValidMessage(event: MessageEvent): void {\n this.logger.info(\n \"Message from configured iframe source and origin received\",\n {\n data: event.data,\n iframeSrc: this.iframeElement?.src,\n },\n );\n\n const message = event.data as AuthMessage;\n const messageType = message?.type;\n\n switch (messageType) {\n case \"auth_success\":\n this.handleAuthSuccess(message);\n break;\n case \"auth_error\":\n this.handleAuthError(message);\n break;\n default:\n this.logger.debug(\n \"Message from iframe did not match expected types (auth_success, auth_error)\",\n { data: event.data },\n );\n }\n }\n\n private handleAuthSuccess(data: AuthMessage): void {\n this.config.events?.emit(AuthEvent.SIGN_IN_COMPLETE, {\n detail: \"Success signal received via postMessage\",\n data,\n });\n this.authPromiseResolve?.((data?.data as AuthResult) || {});\n this.cleanup();\n }\n\n private handleAuthError(data: AuthMessage): void {\n this.config.events?.emit(AuthEvent.SIGN_IN_ERROR, {\n detail: \"Error signal received via postMessage\",\n error: data,\n });\n this.authPromiseReject?.(\n new CivicAuthError(\n data?.detail || \"Error signal received via postMessage\",\n CivicAuthErrorCode.INVALID_MESSAGE,\n ),\n );\n this.cleanup();\n }\n\n private setupSignalObserver(iframeDoc: Document): void {\n const signalObserver = new SignalObserver(\n {\n textSignals: this.config.textSignals,\n events: this.config.events,\n logger: this.logger,\n },\n this.authPromiseResolve,\n this.authPromiseReject,\n () => this.cleanup(),\n );\n\n signalObserver.setup(iframeDoc);\n }\n\n private async handleNewTabAuth(\n fullAuthUrl: string,\n reject: (error: Error) => void,\n ): Promise<void> {\n const popupWindow = window.open(fullAuthUrl, \"_blank\");\n if (!popupWindow) {\n const error = new CivicAuthError(\n \"Failed to open popup window. Please check your browser's popup settings.\",\n CivicAuthErrorCode.INIT_FAILED,\n );\n this.config.events?.emit(AuthEvent.SIGN_IN_ERROR, {\n detail: error.message,\n });\n reject(error);\n }\n }\n\n private async handleIframeAuth(\n fullAuthUrl: string,\n reject: (error: Error) => void,\n ): Promise<void> {\n const container = this.getContainerElement();\n if (!container) {\n const error = new CivicAuthError(\n \"Target container element not found.\",\n CivicAuthErrorCode.CONTAINER_NOT_FOUND,\n );\n this.logger.error(error.message);\n reject(error);\n return;\n }\n\n this.logger.debug(\"Creating iframe\", {\n url: fullAuthUrl,\n containerId: container?.id,\n iframeId: this.config.iframeId,\n origin: window.location.origin,\n });\n\n this.iframeElement = createIframe(\n fullAuthUrl,\n container,\n this.config.iframeId,\n );\n this.config.events?.emit(AuthEvent.SIGN_IN_STARTED, {\n detail: \"Iframe created\",\n });\n\n this.iframeElement.onload = () => {\n this.logger.info(\"Iframe loaded\", {\n iframeSrc: this.iframeElement?.src,\n currentOrigin: window.location.origin,\n expectedAuthServerOrigin: new URL(this.config.oauthServerBaseUrl)\n .origin,\n });\n\n if (!this.iframeElement?.contentWindow) {\n const errorMsg = \"Iframe content window not available after load.\";\n this.logger.error(errorMsg, {\n iframeSrc: this.iframeElement?.src,\n });\n reject(new Error(errorMsg));\n this.cleanup();\n return;\n }\n\n // Set up postMessage listener for cross-origin communication\n if (this.messageEventHandler) {\n window.addEventListener(\"message\", this.messageEventHandler);\n this.logger.info(\n \"Added cross-origin message event listener for auth server communication\",\n {\n parentOrigin: window.location.origin,\n authServerOrigin: new URL(this.config.oauthServerBaseUrl).origin,\n },\n );\n } else {\n this.logger.error(\n \"messageEventHandler was not defined when trying to add listener.\",\n );\n }\n\n // Try to detect redirect to our domain\n try {\n const currentIframeHref =\n this.iframeElement.contentWindow.location.href;\n\n if (currentIframeHref.startsWith(this.config.redirectUrl)) {\n this.logger.info(\n \"Iframe has navigated to redirectUrl (same-origin). Setting up DOM observer.\",\n );\n\n if (\n this.iframeElement.contentDocument &&\n this.iframeElement.contentDocument.body\n ) {\n this.setupSignalObserver(this.iframeElement.contentDocument);\n }\n }\n } catch (error) {\n this.logger.error(\"Error checking iframe href\", {\n error,\n iframeSrc: this.iframeElement?.src,\n });\n // This is expected when the iframe is on the auth server domain\n this.logger.info(\n \"Iframe is on auth server domain - using postMessage for communication\",\n {\n parentOrigin: window.location.origin,\n authServerOrigin: new URL(this.config.oauthServerBaseUrl).origin,\n },\n );\n }\n };\n\n this.iframeElement.onerror = (event) => {\n this.logger.error(\"Iframe load error\", {\n event,\n iframeSrc: this.iframeElement?.src,\n currentOrigin: window.location.origin,\n });\n this.config.events?.emit(AuthEvent.SIGN_IN_ERROR, {\n detail: \"Iframe load error\",\n error: event,\n });\n reject(new Error(\"Iframe failed to load.\"));\n this.cleanup();\n };\n }\n\n /**\n * Starts the authentication process\n * @returns A promise that resolves with the authentication result\n * @throws {CivicAuthError} If authentication fails or times out\n */\n async startAuthentication(): Promise<AuthResult> {\n if (!this.endpoints) {\n const error = new CivicAuthError(\n \"OAuth endpoints not initialized. Please wait for initialization to complete.\",\n CivicAuthErrorCode.ENDPOINTS_NOT_INITIALIZED,\n );\n this.config.events?.emit(AuthEvent.SIGN_IN_ERROR, {\n detail: error.message,\n });\n throw error;\n }\n\n if (this.authPromise) {\n this.logger.info(\n \"Authentication already in progress, returning existing promise\",\n );\n return this.authPromise;\n }\n\n const fullAuthUrl = await this.buildAuthUrl();\n\n this.logger.info(\"Starting authentication process\", {\n constructedIframeUrl: fullAuthUrl,\n displayMode: this.config.displayMode,\n authProcessTimeout: this.config.authProcessTimeout,\n });\n\n this.authPromise = new Promise<AuthResult>((resolve, reject) => {\n this.authPromiseResolve = resolve;\n this.authPromiseReject = reject;\n\n // Handle different display modes\n switch (this.config.displayMode) {\n case \"redirect\":\n // For redirect mode, just navigate to the auth URL\n window.location.href = fullAuthUrl;\n break;\n\n case \"new_tab\":\n this.handleNewTabAuth(fullAuthUrl, reject);\n break;\n\n case \"iframe\":\n default:\n this.handleIframeAuth(fullAuthUrl, reject);\n break;\n }\n\n // Set up timeout for all display modes\n if (\n this.config.authProcessTimeout &&\n this.config.authProcessTimeout > 0\n ) {\n this.logger.debug(\"Setting up authentication timeout\", {\n authProcessTimeout: this.config.authProcessTimeout,\n displayMode: this.config.displayMode,\n });\n this.authProcessTimeoutHandle = window.setTimeout(() => {\n this.logger.warn(\"Authentication timed out\", {\n displayMode: this.config.displayMode,\n currentOrigin: window.location.origin,\n });\n this.config.events?.emit(AuthEvent.SIGN_IN_ERROR, {\n detail: \"Authentication timed out\",\n });\n reject(new Error(\"Authentication timed out.\"));\n this.cleanup();\n }, this.config.authProcessTimeout);\n }\n });\n\n return this.authPromise;\n }\n\n /**\n * Cleans up resources and event listeners\n */\n public cleanup(): void {\n this.logger.info(\"Cleaning up iframe authentication client\");\n\n if (this.observer) {\n this.logger.debug(\"Disconnecting mutation observer\");\n this.observer.disconnect();\n this.observer = undefined;\n }\n\n if (this.messageEventHandler) {\n window.removeEventListener(\"message\", this.messageEventHandler);\n this.logger.debug(\"Removed 'message' event listener from window.\");\n }\n\n if (this.iframeElement) {\n this.logger.debug(\"Removing iframe element\");\n removeIframe(this.iframeElement);\n this.iframeElement = undefined;\n }\n\n if (this.authProcessTimeoutHandle) {\n this.logger.debug(\"Clearing authentication process timeout\");\n window.clearTimeout(this.authProcessTimeoutHandle);\n this.authProcessTimeoutHandle = undefined;\n }\n\n this.logger.debug(\"Resetting promise state\");\n this.authPromise = undefined;\n this.authPromiseResolve = undefined;\n this.authPromiseReject = undefined;\n }\n\n private async handleCallback(): Promise<void> {\n try {\n const callbackHandled = await handleOAuthRedirectPage({\n clientId: this.config.clientId,\n oauthServer: this.config.oauthServerBaseUrl,\n redirectUrl: this.config.redirectUrl,\n textSignals: {\n success: this.config.textSignals.success,\n error: this.config.textSignals.error || \"Authentication failed\",\n },\n });\n\n if (callbackHandled) {\n const successSignal = document.getElementById(\n CONSTANTS.SUCCESS_SIGNAL_ID,\n );\n const errorSignal = document.getElementById(CONSTANTS.ERROR_SIGNAL_ID);\n\n if (successSignal) {\n let userInfo = null;\n const userInfoAttr = successSignal.getAttribute(\"data-user-info\");\n if (userInfoAttr) {\n try {\n userInfo = JSON.parse(userInfoAttr);\n } catch (error) {\n this.logger.error(\"Failed to parse user info:\", { error });\n }\n }\n this.config.events?.emit(AuthEvent.SIGN_IN_COMPLETE, {\n detail: \"Callback processed successfully\",\n user: userInfo,\n });\n } else if (errorSignal) {\n this.config.events?.emit(AuthEvent.SIGN_IN_ERROR, {\n detail: errorSignal.textContent || \"Unknown error during callback\",\n });\n }\n }\n } catch (error) {\n this.config.events?.emit(AuthEvent.SIGN_IN_ERROR, {\n detail:\n error instanceof Error\n ? error.message\n : \"Unknown error during callback\",\n });\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { StorageAdapter } from "../storage/StorageAdapter.js";
|
|
2
|
+
import type { AuthenticationEvents } from "./AuthenticationEvents.js";
|
|
3
|
+
import type { User, Session } from "../types/index.js";
|
|
4
|
+
export declare class SessionManager {
|
|
5
|
+
private storage;
|
|
6
|
+
private events;
|
|
7
|
+
private currentSession;
|
|
8
|
+
constructor(storageAdapter: StorageAdapter, events: AuthenticationEvents);
|
|
9
|
+
loadSession(): Promise<void>;
|
|
10
|
+
setSession(session: Session): Promise<void>;
|
|
11
|
+
clearSession(): Promise<void>;
|
|
12
|
+
getUser(): Promise<User | null>;
|
|
13
|
+
getSession(): Promise<Session | null>;
|
|
14
|
+
isAuthenticated(): Promise<boolean>;
|
|
15
|
+
refreshToken(): Promise<Session | null>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=SessionManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SessionManager.d.ts","sourceRoot":"","sources":["../../../src/vanillajs/auth/SessionManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAMvD,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,cAAc,CAAwB;gBAElC,cAAc,EAAE,cAAc,EAAE,MAAM,EAAE,oBAAoB;IAMlE,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAsB5B,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAS3C,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ7B,OAAO,IAAI,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAK/B,UAAU,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAIrC,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;IAanC,YAAY,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;CAqB9C"}
|