diff --git a/code-examples/sdk/typescript/src/selfservice/registration/apple-social-sign-in-native.ts b/code-examples/sdk/typescript/src/selfservice/registration/apple-social-sign-in-native.ts new file mode 100644 index 000000000..5ffd754ca --- /dev/null +++ b/code-examples/sdk/typescript/src/selfservice/registration/apple-social-sign-in-native.ts @@ -0,0 +1,62 @@ +import { FrontendApi } from "@ory/client" +import * as AppleAuthentication from "expo-apple-authentication" +import * as Crypto from "expo-crypto" + +async function signInWithApplePayload(): Promise<{ + id_token: string + id_token_nonce: string + traits: Record +}> { + const digest = await Crypto.digestStringAsync( + Crypto.CryptoDigestAlgorithm.SHA256, + Crypto.getRandomBytes(16).toString(), + ) + let credential: AppleAuthentication.AppleAuthenticationCredential + try { + credential = await AppleAuthentication.signInAsync({ + requestedScopes: [ + AppleAuthentication.AppleAuthenticationScope.EMAIL, + AppleAuthentication.AppleAuthenticationScope.FULL_NAME, + ], + nonce: digest, + }) + } catch (e) { + console.error("Couldn't sign in with Apple: ", e) + throw e + } + + return { + id_token: credential.identityToken || "", + id_token_nonce: digest, + traits: { + name: { + first: credential.fullName?.givenName || "given name", + last: credential.fullName?.familyName || "last name", + }, + }, + } +} + +export async function signInWithApple(sdk: FrontendApi, flowId: string) { + const payload = await signInWithApplePayload() + return sdk.updateLoginFlow({ + flow: flowId, + updateLoginFlowBody: { + method: "oidc", + provider: "apple", + ...payload, + }, + }) +} + +export async function registerWithApple(sdk: FrontendApi, flowId: string) { + const payload = await signInWithApplePayload() + return sdk.updateRegistrationFlow({ + flow: flowId, + updateRegistrationFlowBody: { + method: "oidc", + provider: "apple", + ...payload, + }, + }) +} diff --git a/docs/kratos/social-signin/30_apple.mdx b/docs/kratos/social-signin/30_apple.mdx index d70be0d8b..895bfd876 100644 --- a/docs/kratos/social-signin/30_apple.mdx +++ b/docs/kratos/social-signin/30_apple.mdx @@ -178,6 +178,42 @@ Follow these steps to add Apple as a social sign-in provider to your project usi ```` +## Using the Apple SDK on native apps + +Apple provides a more integrated UX for native apps using the +[Apple SDK](https://developer.apple.com/documentation/sign_in_with_apple/implementing_user_authentication_with_sign_in_with_apple). +This flow uses the native Apple SDK and does not require a browser. This results in a signed `id_token` on the client side which +is exchanged at Ory for a session token. + +The following steps are required to integrate the Apple SDK with Ory: + +1. Configure an Apple social sign-in provider in Ory using the same `client_id` as in your native app. +2. Generate a random value that you can use as a `nonce`. +3. Obtain an `id_token` from Apple using the Apple SDK. Make sure to also submit the `nonce`. +4. Submit the `id_token` and `nonce` (as the `id_token_nonce`) as part of the `updateRegistrationFlow` or `updateLoginFlow` + request to Ory. +5. Ory will validate the `id_token` and create an identity and optionally a session (if configured). + +The `id_token` is verified using Apple's publicly available signing keys, available under https://appleid.apple.com/auth/keys. + +The `id_token` issued by Apple only contains the user's email address. You can submit additional claims to Ory as part of the +`updateRegistrationFlow` request, as `traits`. + +:::warning + +As Ory does not communicate directly with Apple during this flow, it does not have access to the Access & Refresh Tokens. This +means that Ory cannot return these in the admin APIs or SDK. If you need these tokens, you can exchange the `authorization_code` +returned by Apple on the device manually. + +::: + +```mdx-code-block +import CodeBlock from '@theme/CodeBlock' +import revokeOtherSessionsReact from "!!raw-loader!@site/code-examples/sdk/typescript/src/selfservice/registration/apple-social-sign-in-native.ts" + +{revokeOtherSessionsReact} +``` + ## Troubleshooting ```mdx-code-block diff --git a/docs/kratos/social-signin/96_native-apps.mdx b/docs/kratos/social-signin/96_native-apps.mdx index 84760c29b..ecefeb425 100644 --- a/docs/kratos/social-signin/96_native-apps.mdx +++ b/docs/kratos/social-signin/96_native-apps.mdx @@ -21,6 +21,13 @@ this: - The user authenticates with the identity provider and grants the application access to profile information. - The user is redirected back to the application and is logged in. +:::note + +Apple supports a more tightly integrated login experience for native apps. See the documentation on +[Apple social sign-in](./apple#using-the-apple-sdk-on-native-apps) for more information. + +::: + ## The native app authentication flow From a high level, the native app initializes a login or registration flow and receives the first part of the session token @@ -32,7 +39,7 @@ exchange code for a session token. :::info As part of this flow, Ory will redirect the browser to a callback. In this demo, we use the redirect URL -`http://localhost:4457/Callback`. Please ensure that this +`http://localhost:19006/Callback`. Please ensure that this [redirect URL is allowed](https://console.ory.sh/projects/current/browser-redirects)! :::