1import Constants, { ExecutionEnvironment } from 'expo-constants';
2import * as Linking from 'expo-linking';
3import { Platform } from 'expo-modules-core';
4import { dismissAuthSession } from 'expo-web-browser';
5
6import { AuthRequest } from './AuthRequest';
7import {
8  AuthRequestConfig,
9  AuthRequestPromptOptions,
10  CodeChallengeMethod,
11  Prompt,
12  ResponseType,
13} from './AuthRequest.types';
14import {
15  AuthSessionOptions,
16  AuthSessionRedirectUriOptions,
17  AuthSessionResult,
18} from './AuthSession.types';
19import {
20  DiscoveryDocument,
21  fetchDiscoveryAsync,
22  Issuer,
23  IssuerOrDiscovery,
24  ProviderMetadata,
25  resolveDiscoveryAsync,
26} from './Discovery';
27import { generateHexStringAsync } from './PKCE';
28import sessionUrlProvider from './SessionUrlProvider';
29
30// @needsAudit
31/**
32 * Cancels an active `AuthSession` if there is one.
33 */
34export function dismiss() {
35  dismissAuthSession();
36}
37
38export const getDefaultReturnUrl = sessionUrlProvider.getDefaultReturnUrl;
39
40// @needsAudit @docsMissing
41/**
42 * Get the URL that your authentication provider needs to redirect to. For example: `https://auth.expo.io/@your-username/your-app-slug`. You can pass an additional path component to be appended to the default redirect URL.
43 * > **Note** This method will throw an exception if you're using the bare workflow on native.
44 *
45 * @param path
46 * @return
47 *
48 * @example
49 * ```ts
50 * const url = AuthSession.getRedirectUrl('redirect');
51 *
52 * // Managed: https://auth.expo.io/@your-username/your-app-slug/redirect
53 * // Web: https://localhost:19006/redirect
54 * ```
55 *
56 * @deprecated Use `makeRedirectUri()` instead.
57 */
58export function getRedirectUrl(path?: string): string {
59  return sessionUrlProvider.getRedirectUrl({ urlPath: path });
60}
61
62// @needsAudit
63/**
64 * Create a redirect url for the current platform and environment. You need to manually define the redirect that will be used in
65 * a bare workflow React Native app, or an Expo standalone app, this is because it cannot be inferred automatically.
66 * - **Web:** Generates a path based on the current `window.location`. For production web apps, you should hard code the URL as well.
67 * - **Managed workflow:** Uses the `scheme` property of your app config.
68 * - **Bare workflow:** Will fallback to using the `native` option for bare workflow React Native apps.
69 *
70 * @param options Additional options for configuring the path.
71 * @return The `redirectUri` to use in an authentication request.
72 *
73 * @example
74 * ```ts
75 * const redirectUri = makeRedirectUri({
76 *   scheme: 'my-scheme',
77 *   path: 'redirect'
78 * });
79 * // Development Build: my-scheme://redirect
80 * // Expo Go: exp://127.0.0.1:8081/--/redirect
81 * // Web dev: https://localhost:19006/redirect
82 * // Web prod: https://yourwebsite.com/redirect
83 *
84 * const redirectUri2 = makeRedirectUri({
85 *   scheme: 'scheme2',
86 *   preferLocalhost: true,
87 *   isTripleSlashed: true,
88 * });
89 * // Development Build: scheme2:///
90 * // Expo Go: exp://localhost:8081
91 * // Web dev: https://localhost:19006
92 * // Web prod: https://yourwebsite.com
93 * ```
94 */
95export function makeRedirectUri({
96  native,
97  scheme,
98  isTripleSlashed,
99  queryParams,
100  path,
101  preferLocalhost,
102}: AuthSessionRedirectUriOptions = {}): string {
103  if (
104    Platform.OS !== 'web' &&
105    native &&
106    [ExecutionEnvironment.Standalone, ExecutionEnvironment.Bare].includes(
107      Constants.executionEnvironment
108    )
109  ) {
110    // Should use the user-defined native scheme in standalone builds
111    return native;
112  }
113  const url = Linking.createURL(path || '', {
114    isTripleSlashed,
115    scheme,
116    queryParams,
117  });
118
119  if (preferLocalhost) {
120    const ipAddress = url.match(
121      /\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/
122    );
123    // Only replace if an IP address exists
124    if (ipAddress?.length) {
125      const [protocol, path] = url.split(ipAddress[0]);
126      return `${protocol}localhost${path}`;
127    }
128  }
129
130  return url;
131}
132
133// @needsAudit
134/**
135 * Build an `AuthRequest` and load it before returning.
136 *
137 * @param config A valid [`AuthRequestConfig`](#authrequestconfig) that specifies what provider to use.
138 * @param issuerOrDiscovery A loaded [`DiscoveryDocument`](#discoverydocument) or issuer URL.
139 * (Only `authorizationEndpoint` is required for requesting an authorization code).
140 * @return Returns an instance of `AuthRequest` that can be used to prompt the user for authorization.
141 */
142export async function loadAsync(
143  config: AuthRequestConfig,
144  issuerOrDiscovery: IssuerOrDiscovery
145): Promise<AuthRequest> {
146  const request = new AuthRequest(config);
147  const discovery = await resolveDiscoveryAsync(issuerOrDiscovery);
148  await request.makeAuthUrlAsync(discovery);
149  return request;
150}
151
152export { useAutoDiscovery, useAuthRequest } from './AuthRequestHooks';
153export { AuthError, TokenError } from './Errors';
154
155export {
156  AuthSessionOptions,
157  AuthSessionRedirectUriOptions,
158  AuthSessionResult,
159  AuthRequest,
160  AuthRequestConfig,
161  AuthRequestPromptOptions,
162  CodeChallengeMethod,
163  DiscoveryDocument,
164  Issuer,
165  IssuerOrDiscovery,
166  Prompt,
167  ProviderMetadata,
168  ResponseType,
169  resolveDiscoveryAsync,
170  fetchDiscoveryAsync,
171  generateHexStringAsync,
172};
173
174export {
175  // Token classes
176  TokenResponse,
177  AccessTokenRequest,
178  RefreshTokenRequest,
179  RevokeTokenRequest,
180  // Token methods
181  revokeAsync,
182  refreshAsync,
183  exchangeCodeAsync,
184  fetchUserInfoAsync,
185} from './TokenRequest';
186
187// Token types
188export * from './TokenRequest.types';
189
190// Provider specific types
191export { GoogleAuthRequestConfig } from './providers/Google';
192export { FacebookAuthRequestConfig } from './providers/Facebook';
193