-
Notifications
You must be signed in to change notification settings - Fork 239
Expand file tree
/
Copy pathNativeWebAuthProvider.ts
More file actions
145 lines (128 loc) · 4.82 KB
/
Copy pathNativeWebAuthProvider.ts
File metadata and controls
145 lines (128 loc) · 4.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import { Linking, Platform, type EmitterSubscription } from 'react-native';
import type { IWebAuthProvider } from '../../../core/interfaces';
import type {
Credentials,
WebAuthorizeParameters,
ClearSessionParameters,
User,
} from '../../../types';
import type {
NativeAuthorizeOptions,
NativeClearSessionOptions,
} from '../../../types/platform-specific';
import type { INativeBridge } from '../bridge';
import { finalizeScope } from '../../../core/utils';
import { AuthError, WebAuthError } from '../../../core/models';
const webAuthNotSupported = 'This Method is only available in web platform.';
/**
* A native platform-specific implementation of the IWebAuthProvider.
* This class translates web authentication calls into calls to the native bridge.
*/
export class NativeWebAuthProvider implements IWebAuthProvider {
constructor(
private bridge: INativeBridge,
private domain: string
) {}
handleRedirectCallback(): Promise<void> {
throw new AuthError('NotImplemented', webAuthNotSupported);
}
async checkWebSession() {
throw new AuthError('NotImplemented', webAuthNotSupported);
}
async getWebUser(): Promise<User | null> {
throw new AuthError('NotImplemented', webAuthNotSupported);
}
async authorize(
parameters: WebAuthorizeParameters = {},
options: NativeAuthorizeOptions = {}
): Promise<Credentials> {
let linkSubscription: EmitterSubscription | null = null;
if (Platform.OS === 'ios') {
linkSubscription = Linking.addEventListener('url', async (event) => {
// Only forward URLs whose hostname matches the Auth0 domain.
// This prevents universal links on other domains from being
// incorrectly treated as Auth0 callbacks (e.g. when using
// customScheme: 'https' alongside app-specific universal links).
try {
const url = new URL(event.url);
if (url.hostname !== this.domain) return;
} catch {
return;
}
linkSubscription?.remove();
await this.bridge.resumeWebAuth(event.url);
});
}
try {
// 1. Construct the scheme and redirectUri here.
const scheme =
options.customScheme ??
(await this.getDefaultScheme(options.useLegacyCallbackUrl));
const redirectUri =
parameters.redirectUrl ?? (await this.getCallbackUri(scheme));
const finalScope = finalizeScope(parameters.scope);
// 2. Create the final parameter set for the bridge.
const authParams: WebAuthorizeParameters = {
...parameters,
scope: finalScope,
redirectUrl: redirectUri,
};
// 3. Create the final options set for the bridge.
const authOptions: NativeAuthorizeOptions = {
...options,
customScheme: scheme,
};
// 4. Call the bridge with the finalized parameters and options.
const credentials = await this.bridge.authorize(authParams, authOptions);
// On success, we can safely remove the listener if it hasn't been already.
linkSubscription?.remove();
return credentials;
} catch (error) {
// On error, always clean up the listener.
linkSubscription?.remove();
throw new WebAuthError(error as AuthError);
}
}
async clearSession(
parameters: ClearSessionParameters = {},
options: NativeClearSessionOptions = {}
): Promise<void> {
try {
// 1. Determine the scheme from the `options` object.
const scheme =
options.customScheme ??
(await this.getDefaultScheme(options.useLegacyCallbackUrl));
// 2. Determine the returnToUrl from the `parameters` object, providing a default if needed.
const returnToUrl =
parameters.returnToUrl ?? (await this.getCallbackUri(scheme));
// 3. Prepare the final parameters and options for the bridge.
const finalParameters: ClearSessionParameters = {
...parameters,
returnToUrl,
};
const finalOptions: NativeClearSessionOptions = {
...options,
customScheme: scheme,
};
// 4. Call the bridge with the two separate, finalized objects.
return await this.bridge.clearSession(finalParameters, finalOptions);
} catch (error) {
throw new WebAuthError(error as AuthError);
}
}
async cancelWebAuth(): Promise<void> {
try {
return this.bridge.cancelWebAuth();
} catch (error) {
throw new WebAuthError(error as AuthError);
}
}
private async getDefaultScheme(useLegacy: boolean = false): Promise<string> {
const bundleId = (await this.bridge.getBundleIdentifier()).toLowerCase();
return useLegacy ? bundleId : `${bundleId}.auth0`;
}
private async getCallbackUri(scheme: string): Promise<string> {
const bundleId = (await this.bridge.getBundleIdentifier()).toLowerCase();
return `${scheme}://${this.domain}/${Platform.OS}/${bundleId}/callback`;
}
}