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