1import { CodedError, EventEmitter, Subscription, UnavailabilityError } from 'expo-modules-core'; 2 3import { 4 AppleAuthenticationCredential, 5 AppleAuthenticationCredentialState, 6 AppleAuthenticationOperation, 7 AppleAuthenticationRefreshOptions, 8 AppleAuthenticationSignInOptions, 9 AppleAuthenticationSignOutOptions, 10} from './AppleAuthentication.types'; 11import ExpoAppleAuthentication from './ExpoAppleAuthentication'; 12 13// @needsAudit 14/** 15 * Determine if the current device's operating system supports Apple authentication. 16 * @return A promise that fulfills with `true` if the system supports Apple authentication, and `false` otherwise. 17 */ 18export async function isAvailableAsync(): Promise<boolean> { 19 if (!ExpoAppleAuthentication || !ExpoAppleAuthentication.isAvailableAsync) { 20 return false; 21 } 22 return ExpoAppleAuthentication.isAvailableAsync(); 23} 24 25// @needsAudit 26/** 27 * Sends a request to the operating system to initiate the Apple authentication flow, which will 28 * present a modal to the user over your app and allow them to sign in. 29 * 30 * You can request access to the user's full name and email address in this method, which allows you 31 * to personalize your UI for signed in users. However, users can deny access to either or both 32 * of these options at runtime. 33 * 34 * Additionally, you will only receive Apple Authentication Credentials the first time users sign 35 * into your app, so you must store it for later use. It's best to store this information either 36 * server-side, or using [SecureStore](./securestore), so that the data persists across app installs. 37 * You can use [`AppleAuthenticationCredential.user`](#appleauthenticationcredential) to identify 38 * the user, since this remains the same for apps released by the same developer. 39 * 40 * @param options An optional [`AppleAuthenticationSignInOptions`](#appleauthenticationsigninoptions) object 41 * @return A promise that fulfills with an [`AppleAuthenticationCredential`](#appleauthenticationcredential) 42 * object after a successful authentication, and rejects with `ERR_REQUEST_CANCELED` if the user cancels the 43 * sign-in operation. 44 */ 45export async function signInAsync( 46 options?: AppleAuthenticationSignInOptions 47): Promise<AppleAuthenticationCredential> { 48 if (!ExpoAppleAuthentication || !ExpoAppleAuthentication.requestAsync) { 49 throw new UnavailabilityError('expo-apple-authentication', 'signInAsync'); 50 } 51 const requestOptions = { 52 ...options, 53 requestedOperation: AppleAuthenticationOperation.LOGIN, 54 }; 55 const credential = await ExpoAppleAuthentication.requestAsync(requestOptions); 56 if (!credential.authorizationCode || !credential.identityToken || !credential.user) { 57 throw new CodedError( 58 'ERR_REQUEST_FAILED', 59 'The credential returned by `signInAsync` is missing one or more required fields.' 60 ); 61 } 62 return credential; 63} 64 65// @needsAudit 66/** 67 * An operation that refreshes the logged-in user’s credentials. 68 * Calling this method will show the sign in modal before actually refreshing the user credentials. 69 * 70 * @param options An [`AppleAuthenticationRefreshOptions`](#appleauthenticationrefreshoptions) object 71 * @returns A promise that fulfills with an [`AppleAuthenticationCredential`](#appleauthenticationcredential) 72 * object after a successful authentication, and rejects with `ERR_REQUEST_CANCELED` if the user cancels the 73 * refresh operation. 74 */ 75export async function refreshAsync( 76 options: AppleAuthenticationRefreshOptions 77): Promise<AppleAuthenticationCredential> { 78 if (!ExpoAppleAuthentication || !ExpoAppleAuthentication.requestAsync) { 79 throw new UnavailabilityError('expo-apple-authentication', 'refreshAsync'); 80 } 81 const requestOptions = { 82 ...options, 83 requestedOperation: AppleAuthenticationOperation.REFRESH, 84 }; 85 const credential = await ExpoAppleAuthentication.requestAsync(requestOptions); 86 if (!credential.authorizationCode || !credential.identityToken || !credential.user) { 87 throw new CodedError( 88 'ERR_REQUEST_FAILED', 89 'The credential returned by `refreshAsync` is missing one or more required fields.' 90 ); 91 } 92 return credential; 93} 94 95// @needsAudit 96/** 97 * An operation that ends the authenticated session. 98 * Calling this method will show the sign in modal before actually signing the user out. 99 * 100 * It is not recommended to use this method to sign out the user as it works counterintuitively. 101 * Instead of using this method it is recommended to simply clear all the user's data collected 102 * from using [`signInAsync`](./#signinasync) or [`refreshAsync`](./#refreshasync) methods. 103 * 104 * @param options An [`AppleAuthenticationSignOutOptions`](#appleauthenticationsignoutoptions) object 105 * @returns A promise that fulfills with an [`AppleAuthenticationCredential`](#appleauthenticationcredential) 106 * object after a successful authentication, and rejects with `ERR_REQUEST_CANCELED` if the user cancels the 107 * sign-out operation. 108 */ 109export async function signOutAsync( 110 options: AppleAuthenticationSignOutOptions 111): Promise<AppleAuthenticationCredential> { 112 if (!ExpoAppleAuthentication || !ExpoAppleAuthentication.requestAsync) { 113 throw new UnavailabilityError('expo-apple-authentication', 'signOutAsync'); 114 } 115 const requestOptions = { 116 ...options, 117 requestedOperation: AppleAuthenticationOperation.LOGOUT, 118 }; 119 return ExpoAppleAuthentication.requestAsync(requestOptions); 120} 121 122// @needsAudit 123/** 124 * Queries the current state of a user credential, to determine if it is still valid or if it has been revoked. 125 * > **Note:** This method must be tested on a real device. On the iOS simulator it always throws an error. 126 * 127 * @param user The unique identifier for the user whose credential state you'd like to check. 128 * This should come from the user field of an [`AppleAuthenticationCredential`](#appleauthenticationcredentialstate) object. 129 * @return A promise that fulfills with an [`AppleAuthenticationCredentialState`](#appleauthenticationcredentialstate) 130 * value depending on the state of the credential. 131 */ 132export async function getCredentialStateAsync( 133 user: string 134): Promise<AppleAuthenticationCredentialState> { 135 if (!ExpoAppleAuthentication || !ExpoAppleAuthentication.getCredentialStateAsync) { 136 throw new UnavailabilityError('expo-apple-authentication', 'getCredentialStateAsync'); 137 } 138 return ExpoAppleAuthentication.getCredentialStateAsync(user); 139} 140 141const ExpoAppleAuthenticationEventEmitter = new EventEmitter(ExpoAppleAuthentication); 142 143// @docsMissing 144export function addRevokeListener(listener: () => void): Subscription { 145 return ExpoAppleAuthenticationEventEmitter.addListener('Expo.appleIdCredentialRevoked', listener); 146} 147 148export { Subscription }; 149