@capgo/capacitor-social-login 8.1.1 → 8.2.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.
@@ -0,0 +1,444 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
12
+ import { BaseSocialLogin } from './base';
13
+ /**
14
+ * OAuth2 Social Login Manager
15
+ * Supports multiple OAuth2 provider configurations
16
+ */
17
+ export class OAuth2SocialLogin extends BaseSocialLogin {
18
+ constructor() {
19
+ super(...arguments);
20
+ this.providers = new Map();
21
+ this.TOKENS_KEY_PREFIX = 'capgo_social_login_oauth2_tokens_';
22
+ this.STATE_PREFIX = 'capgo_social_login_oauth2_state_';
23
+ }
24
+ /**
25
+ * Initialize multiple OAuth2 providers
26
+ */
27
+ async initializeProviders(configs) {
28
+ var _a, _b, _c, _d;
29
+ for (const [providerId, config] of Object.entries(configs)) {
30
+ if (!config.appId || !config.authorizationBaseUrl || !config.redirectUrl) {
31
+ throw new Error(`OAuth2 provider '${providerId}' requires appId, authorizationBaseUrl, and redirectUrl`);
32
+ }
33
+ const internalConfig = Object.assign(Object.assign({}, config), { responseType: (_a = config.responseType) !== null && _a !== void 0 ? _a : 'code', pkceEnabled: (_b = config.pkceEnabled) !== null && _b !== void 0 ? _b : true, scope: (_c = config.scope) !== null && _c !== void 0 ? _c : '', logsEnabled: (_d = config.logsEnabled) !== null && _d !== void 0 ? _d : false });
34
+ this.providers.set(providerId, internalConfig);
35
+ if (internalConfig.logsEnabled) {
36
+ console.log(`[OAuth2:${providerId}] Initialized with config:`, {
37
+ appId: config.appId,
38
+ authorizationBaseUrl: config.authorizationBaseUrl,
39
+ redirectUrl: config.redirectUrl,
40
+ responseType: internalConfig.responseType,
41
+ pkceEnabled: internalConfig.pkceEnabled,
42
+ });
43
+ }
44
+ }
45
+ }
46
+ getProvider(providerId) {
47
+ const config = this.providers.get(providerId);
48
+ if (!config) {
49
+ throw new Error(`OAuth2 provider '${providerId}' not configured. Call initialize() first.`);
50
+ }
51
+ return config;
52
+ }
53
+ getTokensKey(providerId) {
54
+ return `${this.TOKENS_KEY_PREFIX}${providerId}`;
55
+ }
56
+ async login(options) {
57
+ var _a, _b, _c, _d;
58
+ const { providerId } = options;
59
+ const config = this.getProvider(providerId);
60
+ const redirectUri = (_a = options.redirectUrl) !== null && _a !== void 0 ? _a : config.redirectUrl;
61
+ const scope = (_b = options.scope) !== null && _b !== void 0 ? _b : config.scope;
62
+ const state = (_c = options.state) !== null && _c !== void 0 ? _c : this.generateState();
63
+ const codeVerifier = (_d = options.codeVerifier) !== null && _d !== void 0 ? _d : this.generateCodeVerifier();
64
+ // Build authorization URL
65
+ const params = new URLSearchParams({
66
+ response_type: config.responseType,
67
+ client_id: config.appId,
68
+ redirect_uri: redirectUri,
69
+ state,
70
+ });
71
+ if (scope) {
72
+ params.set('scope', scope);
73
+ }
74
+ // Add PKCE for code flow
75
+ if (config.responseType === 'code' && config.pkceEnabled) {
76
+ const codeChallenge = await this.generateCodeChallenge(codeVerifier);
77
+ params.set('code_challenge', codeChallenge);
78
+ params.set('code_challenge_method', 'S256');
79
+ }
80
+ // Add additional parameters from config
81
+ if (config.additionalParameters) {
82
+ for (const [key, value] of Object.entries(config.additionalParameters)) {
83
+ params.set(key, value);
84
+ }
85
+ }
86
+ // Add additional parameters from login options
87
+ if (options.additionalParameters) {
88
+ for (const [key, value] of Object.entries(options.additionalParameters)) {
89
+ params.set(key, value);
90
+ }
91
+ }
92
+ // Store pending login state
93
+ this.persistPendingLogin(state, {
94
+ providerId,
95
+ codeVerifier,
96
+ redirectUri,
97
+ scope,
98
+ });
99
+ localStorage.setItem(BaseSocialLogin.OAUTH_STATE_KEY, JSON.stringify({ provider: 'oauth2', providerId, state }));
100
+ const authUrl = `${config.authorizationBaseUrl}?${params.toString()}`;
101
+ if (config.logsEnabled) {
102
+ console.log(`[OAuth2:${providerId}] Opening authorization URL:`, authUrl);
103
+ }
104
+ // Open popup window
105
+ const width = 500;
106
+ const height = 650;
107
+ const left = window.screenX + (window.outerWidth - width) / 2;
108
+ const top = window.screenY + (window.outerHeight - height) / 2;
109
+ const popup = window.open(authUrl, 'OAuth2Login', `width=${width},height=${height},left=${left},top=${top},popup=1`);
110
+ return new Promise((resolve, reject) => {
111
+ if (!popup) {
112
+ reject(new Error('Unable to open login window. Please allow popups.'));
113
+ return;
114
+ }
115
+ const cleanup = (messageHandler, timeoutHandle, intervalHandle) => {
116
+ window.removeEventListener('message', messageHandler);
117
+ clearTimeout(timeoutHandle);
118
+ clearInterval(intervalHandle);
119
+ };
120
+ const messageHandler = (event) => {
121
+ var _a, _b, _c, _d, _e;
122
+ if (event.origin !== window.location.origin) {
123
+ return;
124
+ }
125
+ if (((_a = event.data) === null || _a === void 0 ? void 0 : _a.type) === 'oauth-response') {
126
+ if (((_b = event.data) === null || _b === void 0 ? void 0 : _b.provider) && event.data.provider !== 'oauth2') {
127
+ return;
128
+ }
129
+ // Check providerId matches if present
130
+ if (((_c = event.data) === null || _c === void 0 ? void 0 : _c.providerId) && event.data.providerId !== providerId) {
131
+ return;
132
+ }
133
+ cleanup(messageHandler, timeoutHandle, popupClosedInterval);
134
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
135
+ const _f = event.data, { provider: _ignoredProvider, type: _ignoredType } = _f, payload = __rest(_f, ["provider", "type"]);
136
+ resolve({
137
+ provider: 'oauth2',
138
+ result: payload,
139
+ });
140
+ }
141
+ else if (((_d = event.data) === null || _d === void 0 ? void 0 : _d.type) === 'oauth-error') {
142
+ if (((_e = event.data) === null || _e === void 0 ? void 0 : _e.provider) && event.data.provider !== 'oauth2') {
143
+ return;
144
+ }
145
+ cleanup(messageHandler, timeoutHandle, popupClosedInterval);
146
+ reject(new Error(event.data.error || 'OAuth2 login was cancelled.'));
147
+ }
148
+ };
149
+ window.addEventListener('message', messageHandler);
150
+ const timeoutHandle = window.setTimeout(() => {
151
+ window.removeEventListener('message', messageHandler);
152
+ popup.close();
153
+ reject(new Error('OAuth2 login timed out.'));
154
+ }, 300000);
155
+ const popupClosedInterval = window.setInterval(() => {
156
+ if (popup.closed) {
157
+ window.removeEventListener('message', messageHandler);
158
+ clearInterval(popupClosedInterval);
159
+ clearTimeout(timeoutHandle);
160
+ reject(new Error('OAuth2 login window was closed.'));
161
+ }
162
+ }, 1000);
163
+ });
164
+ }
165
+ async logout(providerId) {
166
+ const config = this.providers.get(providerId);
167
+ localStorage.removeItem(this.getTokensKey(providerId));
168
+ // If logout URL is configured, redirect to it
169
+ if (config === null || config === void 0 ? void 0 : config.logoutUrl) {
170
+ window.open(config.logoutUrl, '_blank');
171
+ }
172
+ }
173
+ async isLoggedIn(providerId) {
174
+ const tokens = this.getStoredTokens(providerId);
175
+ if (!tokens) {
176
+ return { isLoggedIn: false };
177
+ }
178
+ const isValid = tokens.expiresAt > Date.now();
179
+ if (!isValid) {
180
+ localStorage.removeItem(this.getTokensKey(providerId));
181
+ }
182
+ return { isLoggedIn: isValid };
183
+ }
184
+ async getAuthorizationCode(providerId) {
185
+ const tokens = this.getStoredTokens(providerId);
186
+ if (!tokens) {
187
+ throw new Error(`OAuth2 access token is not available for provider '${providerId}'.`);
188
+ }
189
+ return {
190
+ accessToken: tokens.accessToken,
191
+ jwt: tokens.idToken,
192
+ };
193
+ }
194
+ async refresh(providerId) {
195
+ const tokens = this.getStoredTokens(providerId);
196
+ if (!(tokens === null || tokens === void 0 ? void 0 : tokens.refreshToken)) {
197
+ throw new Error(`No OAuth2 refresh token is available for provider '${providerId}'. Include offline_access scope to receive one.`);
198
+ }
199
+ const config = this.getProvider(providerId);
200
+ if (!config.accessTokenEndpoint) {
201
+ throw new Error(`No accessTokenEndpoint configured for provider '${providerId}'.`);
202
+ }
203
+ await this.refreshWithRefreshToken(providerId, tokens.refreshToken);
204
+ }
205
+ async handleOAuthRedirect(url, expectedState) {
206
+ var _a, _b, _c, _d, _e;
207
+ // Check both query params and hash fragment
208
+ const params = new URLSearchParams(url.search);
209
+ const hashParams = new URLSearchParams(url.hash.slice(1));
210
+ // Merge params, hash takes priority (for implicit flow)
211
+ hashParams.forEach((value, key) => {
212
+ params.set(key, value);
213
+ });
214
+ const stateFromUrl = expectedState !== null && expectedState !== void 0 ? expectedState : params.get('state');
215
+ if (!stateFromUrl) {
216
+ return null;
217
+ }
218
+ const pending = this.consumePendingLogin(stateFromUrl);
219
+ if (!pending) {
220
+ localStorage.removeItem(BaseSocialLogin.OAUTH_STATE_KEY);
221
+ return { error: 'OAuth2 login session expired or state mismatch.' };
222
+ }
223
+ const { providerId } = pending;
224
+ const config = this.providers.get(providerId);
225
+ if (!config) {
226
+ localStorage.removeItem(BaseSocialLogin.OAUTH_STATE_KEY);
227
+ return { error: `OAuth2 provider '${providerId}' configuration not found.` };
228
+ }
229
+ const error = params.get('error');
230
+ if (error) {
231
+ localStorage.removeItem(BaseSocialLogin.OAUTH_STATE_KEY);
232
+ return { error: params.get('error_description') || error };
233
+ }
234
+ try {
235
+ let tokenResponse;
236
+ // Check response type
237
+ if (params.has('code')) {
238
+ // Authorization code flow
239
+ const code = params.get('code');
240
+ if (!code) {
241
+ localStorage.removeItem(BaseSocialLogin.OAUTH_STATE_KEY);
242
+ return { error: 'OAuth2 authorization code missing from redirect.' };
243
+ }
244
+ tokenResponse = await this.exchangeAuthorizationCode(providerId, code, pending);
245
+ }
246
+ else if (params.has('access_token')) {
247
+ // Implicit flow
248
+ tokenResponse = {
249
+ access_token: params.get('access_token'),
250
+ token_type: params.get('token_type') || 'bearer',
251
+ expires_in: params.has('expires_in') ? parseInt(params.get('expires_in'), 10) : undefined,
252
+ scope: params.get('scope') || undefined,
253
+ id_token: params.get('id_token') || undefined,
254
+ };
255
+ }
256
+ else {
257
+ localStorage.removeItem(BaseSocialLogin.OAUTH_STATE_KEY);
258
+ return { error: 'No authorization code or access token in redirect.' };
259
+ }
260
+ const expiresAt = tokenResponse.expires_in ? Date.now() + tokenResponse.expires_in * 1000 : Date.now() + 3600000;
261
+ const scopeArray = (_b = (_a = tokenResponse.scope) === null || _a === void 0 ? void 0 : _a.split(' ').filter(Boolean)) !== null && _b !== void 0 ? _b : [];
262
+ // Fetch resource data if configured
263
+ let resourceData = null;
264
+ if (config.resourceUrl) {
265
+ resourceData = await this.fetchResource(providerId, tokenResponse.access_token);
266
+ }
267
+ this.persistTokens(providerId, {
268
+ accessToken: tokenResponse.access_token,
269
+ refreshToken: tokenResponse.refresh_token,
270
+ idToken: tokenResponse.id_token,
271
+ expiresAt,
272
+ scope: scopeArray,
273
+ tokenType: tokenResponse.token_type,
274
+ });
275
+ return {
276
+ provider: 'oauth2',
277
+ result: {
278
+ providerId,
279
+ accessToken: {
280
+ token: tokenResponse.access_token,
281
+ tokenType: tokenResponse.token_type,
282
+ expires: new Date(expiresAt).toISOString(),
283
+ refreshToken: tokenResponse.refresh_token,
284
+ },
285
+ idToken: (_c = tokenResponse.id_token) !== null && _c !== void 0 ? _c : null,
286
+ refreshToken: (_d = tokenResponse.refresh_token) !== null && _d !== void 0 ? _d : null,
287
+ resourceData,
288
+ scope: scopeArray,
289
+ tokenType: tokenResponse.token_type,
290
+ expiresIn: (_e = tokenResponse.expires_in) !== null && _e !== void 0 ? _e : null,
291
+ },
292
+ };
293
+ }
294
+ catch (err) {
295
+ if (err instanceof Error) {
296
+ return { error: err.message };
297
+ }
298
+ return { error: 'OAuth2 login failed unexpectedly.' };
299
+ }
300
+ finally {
301
+ localStorage.removeItem(BaseSocialLogin.OAUTH_STATE_KEY);
302
+ }
303
+ }
304
+ async exchangeAuthorizationCode(providerId, code, pending) {
305
+ const config = this.getProvider(providerId);
306
+ if (!config.accessTokenEndpoint) {
307
+ throw new Error(`No accessTokenEndpoint configured for provider '${providerId}'.`);
308
+ }
309
+ const params = new URLSearchParams({
310
+ grant_type: 'authorization_code',
311
+ client_id: config.appId,
312
+ code,
313
+ redirect_uri: pending.redirectUri,
314
+ });
315
+ if (config.pkceEnabled) {
316
+ params.set('code_verifier', pending.codeVerifier);
317
+ }
318
+ if (config.logsEnabled) {
319
+ console.log(`[OAuth2:${providerId}] Exchanging code at:`, config.accessTokenEndpoint);
320
+ }
321
+ const response = await fetch(config.accessTokenEndpoint, {
322
+ method: 'POST',
323
+ headers: {
324
+ 'Content-Type': 'application/x-www-form-urlencoded',
325
+ },
326
+ body: params.toString(),
327
+ });
328
+ if (!response.ok) {
329
+ const text = await response.text();
330
+ throw new Error(`OAuth2 token exchange failed (${response.status}): ${text}`);
331
+ }
332
+ return (await response.json());
333
+ }
334
+ async refreshWithRefreshToken(providerId, refreshToken) {
335
+ var _a, _b, _c;
336
+ const config = this.getProvider(providerId);
337
+ if (!config.accessTokenEndpoint) {
338
+ throw new Error(`No accessTokenEndpoint configured for provider '${providerId}'.`);
339
+ }
340
+ const params = new URLSearchParams({
341
+ grant_type: 'refresh_token',
342
+ refresh_token: refreshToken,
343
+ client_id: config.appId,
344
+ });
345
+ const response = await fetch(config.accessTokenEndpoint, {
346
+ method: 'POST',
347
+ headers: {
348
+ 'Content-Type': 'application/x-www-form-urlencoded',
349
+ },
350
+ body: params.toString(),
351
+ });
352
+ if (!response.ok) {
353
+ const text = await response.text();
354
+ throw new Error(`OAuth2 refresh failed (${response.status}): ${text}`);
355
+ }
356
+ const tokens = (await response.json());
357
+ const expiresAt = tokens.expires_in ? Date.now() + tokens.expires_in * 1000 : Date.now() + 3600000;
358
+ const scopeArray = (_b = (_a = tokens.scope) === null || _a === void 0 ? void 0 : _a.split(' ').filter(Boolean)) !== null && _b !== void 0 ? _b : [];
359
+ this.persistTokens(providerId, {
360
+ accessToken: tokens.access_token,
361
+ refreshToken: (_c = tokens.refresh_token) !== null && _c !== void 0 ? _c : refreshToken,
362
+ idToken: tokens.id_token,
363
+ expiresAt,
364
+ scope: scopeArray,
365
+ tokenType: tokens.token_type,
366
+ });
367
+ }
368
+ async fetchResource(providerId, accessToken) {
369
+ const config = this.getProvider(providerId);
370
+ if (!config.resourceUrl) {
371
+ throw new Error(`No resourceUrl configured for provider '${providerId}'.`);
372
+ }
373
+ const headers = {
374
+ Authorization: `Bearer ${accessToken}`,
375
+ };
376
+ if (config.additionalResourceHeaders) {
377
+ Object.assign(headers, config.additionalResourceHeaders);
378
+ }
379
+ const response = await fetch(config.resourceUrl, {
380
+ headers,
381
+ });
382
+ if (!response.ok) {
383
+ const text = await response.text();
384
+ throw new Error(`Unable to fetch OAuth2 resource (${response.status}): ${text}`);
385
+ }
386
+ return (await response.json());
387
+ }
388
+ persistTokens(providerId, tokens) {
389
+ localStorage.setItem(this.getTokensKey(providerId), JSON.stringify(tokens));
390
+ }
391
+ getStoredTokens(providerId) {
392
+ const raw = localStorage.getItem(this.getTokensKey(providerId));
393
+ if (!raw) {
394
+ return null;
395
+ }
396
+ try {
397
+ return JSON.parse(raw);
398
+ }
399
+ catch (err) {
400
+ console.warn(`Failed to parse stored OAuth2 tokens for provider '${providerId}'`, err);
401
+ return null;
402
+ }
403
+ }
404
+ persistPendingLogin(state, payload) {
405
+ localStorage.setItem(`${this.STATE_PREFIX}${state}`, JSON.stringify(payload));
406
+ }
407
+ consumePendingLogin(state) {
408
+ const key = `${this.STATE_PREFIX}${state}`;
409
+ const raw = localStorage.getItem(key);
410
+ localStorage.removeItem(key);
411
+ if (!raw) {
412
+ return null;
413
+ }
414
+ try {
415
+ return JSON.parse(raw);
416
+ }
417
+ catch (err) {
418
+ console.warn('Failed to parse pending OAuth2 login payload', err);
419
+ return null;
420
+ }
421
+ }
422
+ generateState() {
423
+ return [...crypto.getRandomValues(new Uint8Array(16))].map((b) => b.toString(16).padStart(2, '0')).join('');
424
+ }
425
+ generateCodeVerifier() {
426
+ const array = new Uint8Array(64);
427
+ crypto.getRandomValues(array);
428
+ return Array.from(array)
429
+ .map((b) => 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~'[b % 66])
430
+ .join('');
431
+ }
432
+ async generateCodeChallenge(codeVerifier) {
433
+ const encoder = new TextEncoder();
434
+ const data = encoder.encode(codeVerifier);
435
+ const digest = await crypto.subtle.digest('SHA-256', data);
436
+ return this.base64UrlEncode(new Uint8Array(digest));
437
+ }
438
+ base64UrlEncode(buffer) {
439
+ let binary = '';
440
+ buffer.forEach((b) => (binary += String.fromCharCode(b)));
441
+ return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
442
+ }
443
+ }
444
+ //# sourceMappingURL=oauth2-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth2-provider.js","sourceRoot":"","sources":["../../src/oauth2-provider.ts"],"names":[],"mappings":"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","sourcesContent":["import { BaseSocialLogin } from './base';\nimport type {\n AuthorizationCode,\n LoginResult,\n OAuth2LoginOptions,\n OAuth2LoginResponse,\n OAuth2ProviderConfig,\n ProviderResponseMap,\n} from './definitions';\n\ninterface OAuth2TokenResponse {\n token_type: string;\n expires_in?: number;\n access_token: string;\n scope?: string;\n refresh_token?: string;\n id_token?: string;\n}\n\ninterface OAuth2PendingLogin {\n providerId: string;\n codeVerifier: string;\n redirectUri: string;\n scope: string;\n}\n\ninterface OAuth2StoredTokens {\n accessToken: string;\n refreshToken?: string;\n idToken?: string;\n expiresAt: number;\n scope: string[];\n tokenType: string;\n}\n\ninterface OAuth2ConfigInternal extends OAuth2ProviderConfig {\n responseType: 'code' | 'token';\n pkceEnabled: boolean;\n scope: string;\n logsEnabled: boolean;\n}\n\n/**\n * OAuth2 Social Login Manager\n * Supports multiple OAuth2 provider configurations\n */\nexport class OAuth2SocialLogin extends BaseSocialLogin {\n private providers: Map<string, OAuth2ConfigInternal> = new Map();\n private readonly TOKENS_KEY_PREFIX = 'capgo_social_login_oauth2_tokens_';\n private readonly STATE_PREFIX = 'capgo_social_login_oauth2_state_';\n\n /**\n * Initialize multiple OAuth2 providers\n */\n async initializeProviders(configs: Record<string, OAuth2ProviderConfig>): Promise<void> {\n for (const [providerId, config] of Object.entries(configs)) {\n if (!config.appId || !config.authorizationBaseUrl || !config.redirectUrl) {\n throw new Error(`OAuth2 provider '${providerId}' requires appId, authorizationBaseUrl, and redirectUrl`);\n }\n\n const internalConfig: OAuth2ConfigInternal = {\n ...config,\n responseType: config.responseType ?? 'code',\n pkceEnabled: config.pkceEnabled ?? true,\n scope: config.scope ?? '',\n logsEnabled: config.logsEnabled ?? false,\n };\n\n this.providers.set(providerId, internalConfig);\n\n if (internalConfig.logsEnabled) {\n console.log(`[OAuth2:${providerId}] Initialized with config:`, {\n appId: config.appId,\n authorizationBaseUrl: config.authorizationBaseUrl,\n redirectUrl: config.redirectUrl,\n responseType: internalConfig.responseType,\n pkceEnabled: internalConfig.pkceEnabled,\n });\n }\n }\n }\n\n private getProvider(providerId: string): OAuth2ConfigInternal {\n const config = this.providers.get(providerId);\n if (!config) {\n throw new Error(`OAuth2 provider '${providerId}' not configured. Call initialize() first.`);\n }\n return config;\n }\n\n private getTokensKey(providerId: string): string {\n return `${this.TOKENS_KEY_PREFIX}${providerId}`;\n }\n\n async login<T extends 'oauth2'>(\n options: OAuth2LoginOptions,\n ): Promise<{ provider: T; result: ProviderResponseMap[T] }> {\n const { providerId } = options;\n const config = this.getProvider(providerId);\n\n const redirectUri = options.redirectUrl ?? config.redirectUrl;\n const scope = options.scope ?? config.scope;\n const state = options.state ?? this.generateState();\n const codeVerifier = options.codeVerifier ?? this.generateCodeVerifier();\n\n // Build authorization URL\n const params = new URLSearchParams({\n response_type: config.responseType,\n client_id: config.appId,\n redirect_uri: redirectUri,\n state,\n });\n\n if (scope) {\n params.set('scope', scope);\n }\n\n // Add PKCE for code flow\n if (config.responseType === 'code' && config.pkceEnabled) {\n const codeChallenge = await this.generateCodeChallenge(codeVerifier);\n params.set('code_challenge', codeChallenge);\n params.set('code_challenge_method', 'S256');\n }\n\n // Add additional parameters from config\n if (config.additionalParameters) {\n for (const [key, value] of Object.entries(config.additionalParameters)) {\n params.set(key, value);\n }\n }\n\n // Add additional parameters from login options\n if (options.additionalParameters) {\n for (const [key, value] of Object.entries(options.additionalParameters)) {\n params.set(key, value);\n }\n }\n\n // Store pending login state\n this.persistPendingLogin(state, {\n providerId,\n codeVerifier,\n redirectUri,\n scope,\n });\n\n localStorage.setItem(BaseSocialLogin.OAUTH_STATE_KEY, JSON.stringify({ provider: 'oauth2', providerId, state }));\n\n const authUrl = `${config.authorizationBaseUrl}?${params.toString()}`;\n\n if (config.logsEnabled) {\n console.log(`[OAuth2:${providerId}] Opening authorization URL:`, authUrl);\n }\n\n // Open popup window\n const width = 500;\n const height = 650;\n const left = window.screenX + (window.outerWidth - width) / 2;\n const top = window.screenY + (window.outerHeight - height) / 2;\n\n const popup = window.open(\n authUrl,\n 'OAuth2Login',\n `width=${width},height=${height},left=${left},top=${top},popup=1`,\n );\n\n return new Promise((resolve, reject) => {\n if (!popup) {\n reject(new Error('Unable to open login window. Please allow popups.'));\n return;\n }\n\n const cleanup = (\n messageHandler: (event: MessageEvent) => void,\n timeoutHandle: number,\n intervalHandle: number,\n ) => {\n window.removeEventListener('message', messageHandler);\n clearTimeout(timeoutHandle);\n clearInterval(intervalHandle);\n };\n\n const messageHandler = (event: MessageEvent) => {\n if (event.origin !== window.location.origin) {\n return;\n }\n if (event.data?.type === 'oauth-response') {\n if (event.data?.provider && event.data.provider !== 'oauth2') {\n return;\n }\n // Check providerId matches if present\n if (event.data?.providerId && event.data.providerId !== providerId) {\n return;\n }\n cleanup(messageHandler, timeoutHandle, popupClosedInterval);\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const {\n provider: _ignoredProvider,\n type: _ignoredType,\n ...payload\n } = event.data as OAuth2LoginResponse & {\n provider?: string;\n type?: string;\n };\n resolve({\n provider: 'oauth2' as T,\n result: payload as ProviderResponseMap[T],\n } as { provider: T; result: ProviderResponseMap[T] });\n } else if (event.data?.type === 'oauth-error') {\n if (event.data?.provider && event.data.provider !== 'oauth2') {\n return;\n }\n cleanup(messageHandler, timeoutHandle, popupClosedInterval);\n reject(new Error(event.data.error || 'OAuth2 login was cancelled.'));\n }\n };\n\n window.addEventListener('message', messageHandler);\n\n const timeoutHandle = window.setTimeout(() => {\n window.removeEventListener('message', messageHandler);\n popup.close();\n reject(new Error('OAuth2 login timed out.'));\n }, 300000);\n\n const popupClosedInterval = window.setInterval(() => {\n if (popup.closed) {\n window.removeEventListener('message', messageHandler);\n clearInterval(popupClosedInterval);\n clearTimeout(timeoutHandle);\n reject(new Error('OAuth2 login window was closed.'));\n }\n }, 1000);\n });\n }\n\n async logout(providerId: string): Promise<void> {\n const config = this.providers.get(providerId);\n localStorage.removeItem(this.getTokensKey(providerId));\n\n // If logout URL is configured, redirect to it\n if (config?.logoutUrl) {\n window.open(config.logoutUrl, '_blank');\n }\n }\n\n async isLoggedIn(providerId: string): Promise<{ isLoggedIn: boolean }> {\n const tokens = this.getStoredTokens(providerId);\n if (!tokens) {\n return { isLoggedIn: false };\n }\n const isValid = tokens.expiresAt > Date.now();\n if (!isValid) {\n localStorage.removeItem(this.getTokensKey(providerId));\n }\n return { isLoggedIn: isValid };\n }\n\n async getAuthorizationCode(providerId: string): Promise<AuthorizationCode> {\n const tokens = this.getStoredTokens(providerId);\n if (!tokens) {\n throw new Error(`OAuth2 access token is not available for provider '${providerId}'.`);\n }\n return {\n accessToken: tokens.accessToken,\n jwt: tokens.idToken,\n };\n }\n\n async refresh(providerId: string): Promise<void> {\n const tokens = this.getStoredTokens(providerId);\n if (!tokens?.refreshToken) {\n throw new Error(\n `No OAuth2 refresh token is available for provider '${providerId}'. Include offline_access scope to receive one.`,\n );\n }\n\n const config = this.getProvider(providerId);\n if (!config.accessTokenEndpoint) {\n throw new Error(`No accessTokenEndpoint configured for provider '${providerId}'.`);\n }\n\n await this.refreshWithRefreshToken(providerId, tokens.refreshToken);\n }\n\n async handleOAuthRedirect(url: URL, expectedState?: string): Promise<LoginResult | { error: string } | null> {\n // Check both query params and hash fragment\n const params = new URLSearchParams(url.search);\n const hashParams = new URLSearchParams(url.hash.slice(1));\n\n // Merge params, hash takes priority (for implicit flow)\n hashParams.forEach((value, key) => {\n params.set(key, value);\n });\n\n const stateFromUrl = expectedState ?? params.get('state');\n if (!stateFromUrl) {\n return null;\n }\n\n const pending = this.consumePendingLogin(stateFromUrl);\n if (!pending) {\n localStorage.removeItem(BaseSocialLogin.OAUTH_STATE_KEY);\n return { error: 'OAuth2 login session expired or state mismatch.' };\n }\n\n const { providerId } = pending;\n const config = this.providers.get(providerId);\n if (!config) {\n localStorage.removeItem(BaseSocialLogin.OAUTH_STATE_KEY);\n return { error: `OAuth2 provider '${providerId}' configuration not found.` };\n }\n\n const error = params.get('error');\n if (error) {\n localStorage.removeItem(BaseSocialLogin.OAUTH_STATE_KEY);\n return { error: params.get('error_description') || error };\n }\n\n try {\n let tokenResponse: OAuth2TokenResponse;\n\n // Check response type\n if (params.has('code')) {\n // Authorization code flow\n const code = params.get('code');\n if (!code) {\n localStorage.removeItem(BaseSocialLogin.OAUTH_STATE_KEY);\n return { error: 'OAuth2 authorization code missing from redirect.' };\n }\n tokenResponse = await this.exchangeAuthorizationCode(providerId, code, pending);\n } else if (params.has('access_token')) {\n // Implicit flow\n tokenResponse = {\n access_token: params.get('access_token')!,\n token_type: params.get('token_type') || 'bearer',\n expires_in: params.has('expires_in') ? parseInt(params.get('expires_in')!, 10) : undefined,\n scope: params.get('scope') || undefined,\n id_token: params.get('id_token') || undefined,\n };\n } else {\n localStorage.removeItem(BaseSocialLogin.OAUTH_STATE_KEY);\n return { error: 'No authorization code or access token in redirect.' };\n }\n\n const expiresAt = tokenResponse.expires_in ? Date.now() + tokenResponse.expires_in * 1000 : Date.now() + 3600000;\n const scopeArray = tokenResponse.scope?.split(' ').filter(Boolean) ?? [];\n\n // Fetch resource data if configured\n let resourceData: Record<string, unknown> | null = null;\n if (config.resourceUrl) {\n resourceData = await this.fetchResource(providerId, tokenResponse.access_token);\n }\n\n this.persistTokens(providerId, {\n accessToken: tokenResponse.access_token,\n refreshToken: tokenResponse.refresh_token,\n idToken: tokenResponse.id_token,\n expiresAt,\n scope: scopeArray,\n tokenType: tokenResponse.token_type,\n });\n\n return {\n provider: 'oauth2',\n result: {\n providerId,\n accessToken: {\n token: tokenResponse.access_token,\n tokenType: tokenResponse.token_type,\n expires: new Date(expiresAt).toISOString(),\n refreshToken: tokenResponse.refresh_token,\n },\n idToken: tokenResponse.id_token ?? null,\n refreshToken: tokenResponse.refresh_token ?? null,\n resourceData,\n scope: scopeArray,\n tokenType: tokenResponse.token_type,\n expiresIn: tokenResponse.expires_in ?? null,\n },\n };\n } catch (err) {\n if (err instanceof Error) {\n return { error: err.message };\n }\n return { error: 'OAuth2 login failed unexpectedly.' };\n } finally {\n localStorage.removeItem(BaseSocialLogin.OAUTH_STATE_KEY);\n }\n }\n\n private async exchangeAuthorizationCode(\n providerId: string,\n code: string,\n pending: OAuth2PendingLogin,\n ): Promise<OAuth2TokenResponse> {\n const config = this.getProvider(providerId);\n if (!config.accessTokenEndpoint) {\n throw new Error(`No accessTokenEndpoint configured for provider '${providerId}'.`);\n }\n\n const params = new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: config.appId,\n code,\n redirect_uri: pending.redirectUri,\n });\n\n if (config.pkceEnabled) {\n params.set('code_verifier', pending.codeVerifier);\n }\n\n if (config.logsEnabled) {\n console.log(`[OAuth2:${providerId}] Exchanging code at:`, config.accessTokenEndpoint);\n }\n\n const response = await fetch(config.accessTokenEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: params.toString(),\n });\n\n if (!response.ok) {\n const text = await response.text();\n throw new Error(`OAuth2 token exchange failed (${response.status}): ${text}`);\n }\n\n return (await response.json()) as OAuth2TokenResponse;\n }\n\n private async refreshWithRefreshToken(providerId: string, refreshToken: string): Promise<void> {\n const config = this.getProvider(providerId);\n if (!config.accessTokenEndpoint) {\n throw new Error(`No accessTokenEndpoint configured for provider '${providerId}'.`);\n }\n\n const params = new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n client_id: config.appId,\n });\n\n const response = await fetch(config.accessTokenEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: params.toString(),\n });\n\n if (!response.ok) {\n const text = await response.text();\n throw new Error(`OAuth2 refresh failed (${response.status}): ${text}`);\n }\n\n const tokens = (await response.json()) as OAuth2TokenResponse;\n const expiresAt = tokens.expires_in ? Date.now() + tokens.expires_in * 1000 : Date.now() + 3600000;\n const scopeArray = tokens.scope?.split(' ').filter(Boolean) ?? [];\n\n this.persistTokens(providerId, {\n accessToken: tokens.access_token,\n refreshToken: tokens.refresh_token ?? refreshToken,\n idToken: tokens.id_token,\n expiresAt,\n scope: scopeArray,\n tokenType: tokens.token_type,\n });\n }\n\n private async fetchResource(providerId: string, accessToken: string): Promise<Record<string, unknown>> {\n const config = this.getProvider(providerId);\n if (!config.resourceUrl) {\n throw new Error(`No resourceUrl configured for provider '${providerId}'.`);\n }\n\n const headers: Record<string, string> = {\n Authorization: `Bearer ${accessToken}`,\n };\n\n if (config.additionalResourceHeaders) {\n Object.assign(headers, config.additionalResourceHeaders);\n }\n\n const response = await fetch(config.resourceUrl, {\n headers,\n });\n\n if (!response.ok) {\n const text = await response.text();\n throw new Error(`Unable to fetch OAuth2 resource (${response.status}): ${text}`);\n }\n\n return (await response.json()) as Record<string, unknown>;\n }\n\n private persistTokens(providerId: string, tokens: OAuth2StoredTokens): void {\n localStorage.setItem(this.getTokensKey(providerId), JSON.stringify(tokens));\n }\n\n private getStoredTokens(providerId: string): OAuth2StoredTokens | null {\n const raw = localStorage.getItem(this.getTokensKey(providerId));\n if (!raw) {\n return null;\n }\n try {\n return JSON.parse(raw) as OAuth2StoredTokens;\n } catch (err) {\n console.warn(`Failed to parse stored OAuth2 tokens for provider '${providerId}'`, err);\n return null;\n }\n }\n\n private persistPendingLogin(state: string, payload: OAuth2PendingLogin): void {\n localStorage.setItem(`${this.STATE_PREFIX}${state}`, JSON.stringify(payload));\n }\n\n private consumePendingLogin(state: string): OAuth2PendingLogin | null {\n const key = `${this.STATE_PREFIX}${state}`;\n const raw = localStorage.getItem(key);\n localStorage.removeItem(key);\n if (!raw) {\n return null;\n }\n try {\n return JSON.parse(raw) as OAuth2PendingLogin;\n } catch (err) {\n console.warn('Failed to parse pending OAuth2 login payload', err);\n return null;\n }\n }\n\n private generateState(): string {\n return [...crypto.getRandomValues(new Uint8Array(16))].map((b) => b.toString(16).padStart(2, '0')).join('');\n }\n\n private generateCodeVerifier(): string {\n const array = new Uint8Array(64);\n crypto.getRandomValues(array);\n return Array.from(array)\n .map((b) => 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~'[b % 66])\n .join('');\n }\n\n private async generateCodeChallenge(codeVerifier: string): Promise<string> {\n const encoder = new TextEncoder();\n const data = encoder.encode(codeVerifier);\n const digest = await crypto.subtle.digest('SHA-256', data);\n return this.base64UrlEncode(new Uint8Array(digest));\n }\n\n private base64UrlEncode(buffer: Uint8Array): string {\n let binary = '';\n buffer.forEach((b) => (binary += String.fromCharCode(b)));\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n }\n}\n"]}
package/dist/esm/web.d.ts CHANGED
@@ -6,6 +6,7 @@ export declare class SocialLoginWeb extends WebPlugin implements SocialLoginPlug
6
6
  private appleProvider;
7
7
  private facebookProvider;
8
8
  private twitterProvider;
9
+ private oauth2Provider;
9
10
  constructor();
10
11
  private handleOAuthRedirect;
11
12
  initialize(options: InitializeOptions): Promise<void>;
@@ -16,7 +17,8 @@ export declare class SocialLoginWeb extends WebPlugin implements SocialLoginPlug
16
17
  result: ProviderResponseMap[T];
17
18
  }>;
18
19
  logout(options: {
19
- provider: 'apple' | 'google' | 'facebook' | 'twitter';
20
+ provider: 'apple' | 'google' | 'facebook' | 'twitter' | 'oauth2';
21
+ providerId?: string;
20
22
  }): Promise<void>;
21
23
  isLoggedIn(options: isLoggedInOptions): Promise<{
22
24
  isLoggedIn: boolean;
package/dist/esm/web.js CHANGED
@@ -2,6 +2,7 @@ import { WebPlugin } from '@capacitor/core';
2
2
  import { AppleSocialLogin } from './apple-provider';
3
3
  import { FacebookSocialLogin } from './facebook-provider';
4
4
  import { GoogleSocialLogin } from './google-provider';
5
+ import { OAuth2SocialLogin } from './oauth2-provider';
5
6
  import { TwitterSocialLogin } from './twitter-provider';
6
7
  export class SocialLoginWeb extends WebPlugin {
7
8
  constructor() {
@@ -10,6 +11,7 @@ export class SocialLoginWeb extends WebPlugin {
10
11
  this.appleProvider = new AppleSocialLogin();
11
12
  this.facebookProvider = new FacebookSocialLogin();
12
13
  this.twitterProvider = new TwitterSocialLogin();
14
+ this.oauth2Provider = new OAuth2SocialLogin();
13
15
  // Set up listener for OAuth redirects if we have a pending OAuth flow
14
16
  if (localStorage.getItem(SocialLoginWeb.OAUTH_STATE_KEY)) {
15
17
  console.log('OAUTH_STATE_KEY found');
@@ -40,6 +42,9 @@ export class SocialLoginWeb extends WebPlugin {
40
42
  case 'twitter':
41
43
  result = await this.twitterProvider.handleOAuthRedirect(url, state);
42
44
  break;
45
+ case 'oauth2':
46
+ result = await this.oauth2Provider.handleOAuthRedirect(url, state);
47
+ break;
43
48
  case 'google':
44
49
  default:
45
50
  result = this.googleProvider.handleOAuthRedirect(url);
@@ -76,6 +81,9 @@ export class SocialLoginWeb extends WebPlugin {
76
81
  if ((_d = options.twitter) === null || _d === void 0 ? void 0 : _d.clientId) {
77
82
  initPromises.push(this.twitterProvider.initialize(options.twitter.clientId, options.twitter.redirectUrl, options.twitter.defaultScopes, options.twitter.forceLogin, options.twitter.audience));
78
83
  }
84
+ if (options.oauth2 && Object.keys(options.oauth2).length > 0) {
85
+ initPromises.push(this.oauth2Provider.initializeProviders(options.oauth2));
86
+ }
79
87
  await Promise.all(initPromises);
80
88
  }
81
89
  async login(options) {
@@ -88,6 +96,8 @@ export class SocialLoginWeb extends WebPlugin {
88
96
  return this.facebookProvider.login(options.options);
89
97
  case 'twitter':
90
98
  return this.twitterProvider.login(options.options);
99
+ case 'oauth2':
100
+ return this.oauth2Provider.login(options.options);
91
101
  default:
92
102
  throw new Error(`Login for ${options.provider} is not implemented on web`);
93
103
  }
@@ -102,6 +112,11 @@ export class SocialLoginWeb extends WebPlugin {
102
112
  return this.facebookProvider.logout();
103
113
  case 'twitter':
104
114
  return this.twitterProvider.logout();
115
+ case 'oauth2':
116
+ if (!options.providerId) {
117
+ throw new Error('providerId is required for oauth2 logout');
118
+ }
119
+ return this.oauth2Provider.logout(options.providerId);
105
120
  default:
106
121
  throw new Error(`Logout for ${options.provider} is not implemented`);
107
122
  }
@@ -116,6 +131,11 @@ export class SocialLoginWeb extends WebPlugin {
116
131
  return this.facebookProvider.isLoggedIn();
117
132
  case 'twitter':
118
133
  return this.twitterProvider.isLoggedIn();
134
+ case 'oauth2':
135
+ if (!options.providerId) {
136
+ throw new Error('providerId is required for oauth2 isLoggedIn');
137
+ }
138
+ return this.oauth2Provider.isLoggedIn(options.providerId);
119
139
  default:
120
140
  throw new Error(`isLoggedIn for ${options.provider} is not implemented`);
121
141
  }
@@ -130,6 +150,11 @@ export class SocialLoginWeb extends WebPlugin {
130
150
  return this.facebookProvider.getAuthorizationCode();
131
151
  case 'twitter':
132
152
  return this.twitterProvider.getAuthorizationCode();
153
+ case 'oauth2':
154
+ if (!options.providerId) {
155
+ throw new Error('providerId is required for oauth2 getAuthorizationCode');
156
+ }
157
+ return this.oauth2Provider.getAuthorizationCode(options.providerId);
133
158
  default:
134
159
  throw new Error(`getAuthorizationCode for ${options.provider} is not implemented`);
135
160
  }
@@ -144,6 +169,13 @@ export class SocialLoginWeb extends WebPlugin {
144
169
  return this.facebookProvider.refresh(options.options);
145
170
  case 'twitter':
146
171
  return this.twitterProvider.refresh();
172
+ case 'oauth2': {
173
+ const oauth2Options = options.options;
174
+ if (!(oauth2Options === null || oauth2Options === void 0 ? void 0 : oauth2Options.providerId)) {
175
+ throw new Error('providerId is required for oauth2 refresh');
176
+ }
177
+ return this.oauth2Provider.refresh(oauth2Options.providerId);
178
+ }
147
179
  default:
148
180
  throw new Error(`Refresh for ${options.provider} is not implemented`);
149
181
  }