Last active
December 9, 2021 17:47
-
-
Save nandorojo/f1ccae6711f37e1352583f6a557b8cb8 to your computer and use it in GitHub Desktop.
Expo Apple Auth
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import type { | |
AppleAuthenticationButtonStyle, | |
AppleAuthenticationButtonType, | |
AppleAuthenticationUserDetectionStatus, | |
AppleAuthenticationButtonProps, | |
AppleAuthenticationCredential, | |
AppleAuthenticationSignInOptions, | |
} from 'expo-apple-authentication' | |
import { Image, Platform, Pressable, StyleSheet, Text } from 'react-native' | |
import { useEffect } from 'react' | |
// downloaded from https://developer.apple.com/design/resources/ | |
import logoWhite from './logos/Logo - SIWA - Logo-only - [email protected]' | |
import logoBlack from './logos/Logo - SIWA - Logo-only - [email protected]' | |
const signInAsync = async ( | |
options?: AppleAuthenticationSignInOptions | |
): Promise<AppleAuthenticationCredential> => { | |
const { nonce, requestedScopes, state } = options || {} | |
const scope = requestedScopes?.join(' ') | |
const { authorization, user } = await AppleID.auth.signIn({ | |
nonce, | |
scope, | |
state, | |
}) | |
return { | |
authorizationCode: authorization.code, | |
identityToken: authorization.id_token, | |
state: authorization.state, | |
email: user?.email || null, | |
fullName: { | |
familyName: user?.name.lastName || null, | |
givenName: user?.name.firstName || null, | |
nickname: null, | |
middleName: null, | |
namePrefix: null, | |
nameSuffix: null, | |
}, | |
realUserStatus: AppleAuthenticationUserDetectionStatus.UNSUPPORTED, | |
user: '', // TODO document that this is iOS-only | |
} | |
} | |
// https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/buttons/#custom-buttons-with-a-logo-and-text | |
const getFontSize = (buttonHeight: number) => Math.round(buttonHeight * 0.43) | |
// https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/buttons/ | |
const buttonHeight = 44 | |
const buttonWidth = 200 | |
const AppleAuthenticationButton = ( | |
props: AppleAuthenticationButtonProps & { | |
web?: Parameters<typeof AppleID.auth.init>[0] | |
} | |
) => { | |
const { | |
onPress, | |
buttonStyle, | |
buttonType, | |
cornerRadius = 5, | |
web, | |
style, | |
accessibilityRole, | |
...rest | |
} = props | |
useEffect(() => { | |
if (Platform.OS === 'web') { | |
const script = document.createElement('script') | |
script.src = | |
'https://appleid.cdn-apple.com/appleauth/static/jsapi/appleid/1/en_US/appleid.auth.js' | |
document.body.appendChild(script) | |
const onLoad = () => { | |
if (!web) { | |
console.error('AppleAuthenticationButton is missing "web" prop.') | |
} | |
AppleID.auth.init(web || {}) | |
} | |
script.addEventListener('load', onLoad) | |
return () => { | |
script.removeEventListener('load', onLoad) | |
document.body.removeChild(script) | |
} | |
} | |
// eslint-disable-next-line react-hooks/exhaustive-deps | |
}, []) | |
let flatStyle = StyleSheet.flatten([style]) as any | |
flatStyle = flatStyle && typeof flatStyle == 'object' ? flatStyle : undefined | |
const height: number = flatStyle?.height || buttonHeight | |
const fontSize = getFontSize(height) | |
const lineHeight = height | |
// https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/buttons/ | |
return ( | |
<Pressable | |
// @ts-expect-error Pressable & View have different accessiblityRole types (View's is just string) | |
accessibilityRole={accessibilityRole} | |
style={[ | |
styles.button, | |
styles[buttonStyle], | |
{ borderRadius: cornerRadius }, | |
style, | |
]} | |
onPress={onPress} | |
{...rest} | |
> | |
<Image | |
source={ | |
buttonStyle === AppleAuthenticationButtonStyle.BLACK | |
? logoWhite | |
: logoBlack | |
} | |
height={height} | |
width={height} | |
style={{ | |
height, | |
width: height, | |
}} | |
/> | |
<Text | |
style={[ | |
textStyles.text, | |
{ lineHeight, fontSize }, | |
textStyles[buttonType], | |
]} | |
> | |
{ButtonCopy[buttonType]} | |
</Text> | |
</Pressable> | |
) | |
} | |
export const ButtonCopy = { | |
[AppleAuthenticationButtonType.CONTINUE]: 'Continue with Apple', | |
[AppleAuthenticationButtonType.SIGN_IN]: 'Sign in with Apple', | |
[AppleAuthenticationButtonType.SIGN_UP]: 'Sign up with Apple', | |
} | |
const styles = StyleSheet.create({ | |
button: { | |
flexDirection: 'row', | |
justifyContent: 'flex-start', | |
alignItems: 'center', | |
width: buttonWidth, | |
height: buttonHeight, | |
minWidth: 140, | |
minHeight: 30, | |
overflow: 'hidden', | |
}, | |
[AppleAuthenticationButtonStyle.WHITE]: { | |
backgroundColor: '#fff', | |
}, | |
[AppleAuthenticationButtonStyle.WHITE_OUTLINE]: { | |
backgroundColor: '#fff', | |
borderWidth: 1, | |
borderColor: '#000', | |
}, | |
[AppleAuthenticationButtonStyle.BLACK]: { | |
backgroundColor: '#000', | |
}, | |
}) | |
const textStyles = StyleSheet.create({ | |
text: { | |
fontSize: 15, | |
}, | |
[AppleAuthenticationButtonStyle.WHITE]: { | |
color: '#000', | |
}, | |
[AppleAuthenticationButtonStyle.WHITE_OUTLINE]: { | |
color: '#000', | |
}, | |
[AppleAuthenticationButtonStyle.BLACK]: { | |
color: '#fff', | |
}, | |
}) | |
export { AppleAuthenticationButton, signInAsync } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Type definitions for non-npm package Apple Sign in API 1.5 | |
// Project: https://developer.apple.com/documentation/signinwithapplejs | |
// Definitions by: Julius Lungys <https://github.com/voidpumpkin> | |
// Koen Punt <https://github.com/koenpunt> | |
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped | |
declare const AppleID: AppleSignInAPI.AppleID | |
declare namespace AppleSignInAPI { | |
// https://developer.apple.com/documentation/signinwithapplejs/authorizationi | |
interface AuthorizationI { | |
code: string | |
id_token: string | |
state: string | |
nonce?: string | undefined | |
} | |
// https://developer.apple.com/documentation/signinwithapplejs/namei | |
interface NameI { | |
firstName: string | |
lastName: string | |
} | |
// https://developer.apple.com/documentation/signinwithapplejs/signinerrori | |
interface SignInErrorI { | |
error: string | |
} | |
// https://developer.apple.com/documentation/signinwithapplejs/signinresponsei | |
interface SignInResponseI { | |
authorization: AuthorizationI | |
user?: UserI | undefined | |
} | |
// https://developer.apple.com/documentation/signinwithapplejs/useri | |
interface UserI { | |
email: string | |
name: NameI | |
} | |
// https://developer.apple.com/documentation/signinwithapplejs/authi | |
interface AuthI { | |
init: (config: ClientConfigI) => void | |
signIn: (signInConfig?: ClientConfigI) => Promise<SignInResponseI> | |
renderButton: () => void | |
} | |
// https://developer.apple.com/documentation/signinwithapplejs/clientconfigi | |
interface ClientConfigI { | |
clientId?: string | undefined | |
redirectURI?: string | undefined | |
scope?: string | undefined | |
state?: string | undefined | |
nonce?: string | undefined | |
usePopup?: boolean | undefined | |
} | |
interface AppleID { | |
auth: AuthI | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment