From 8740056dfa7acdf0ab7201d0cac97cb2cb9dbb6e Mon Sep 17 00:00:00 2001 From: Jonas Hungershausen Date: Wed, 6 Sep 2023 18:29:07 +0200 Subject: [PATCH 1/3] feat: add docs for native sign in using Apple SDK --- .../apple-social-sign-in-native.ts | 34 ++++++++++++++++++ docs/kratos/social-signin/30_apple.mdx | 36 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 code-examples/sdk/typescript/src/selfservice/registration/apple-social-sign-in-native.ts 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..7465bb3ba --- /dev/null +++ b/code-examples/sdk/typescript/src/selfservice/registration/apple-social-sign-in-native.ts @@ -0,0 +1,34 @@ +import * as AppleAuthentication from "expo-apple-authentication" +import * as Crypto from "expo-crypto" +import * as Random from "expo-random" + +async function signInWithApple() { + const nonce = Random.getRandomBytes(16).toString() + + const digest = await Crypto.digestStringAsync( + Crypto.CryptoDigestAlgorithm.SHA256, + nonce, + ) + const credential = await AppleAuthentication.signInAsync({ + requestedScopes: [ + AppleAuthentication.AppleAuthenticationScope.EMAIL, + AppleAuthentication.AppleAuthenticationScope.FULL_NAME, + ], + nonce: digest, + }) + + return orySdk.updateRegistrationFlow({ + flow: flow.id, + updateRegistrationFlowBody: { + provider: "apple", + id_token: credential.identityToken, + raw_id_token_nonce: nonce, + traits: { + name: { + first: credential.fullName?.givenName || "given name", // When developing, these values might be empty + last: credential.fullName?.familyName || "last name", // When developing, these values might be empty + }, + }, + }, + }) +} diff --git a/docs/kratos/social-signin/30_apple.mdx b/docs/kratos/social-signin/30_apple.mdx index d70be0d8b..b802824a7 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). +Since this flow does not involve a browser and redirect URL, but instead issues an `id_token`, it requires a bit more work to +integrate with Ory. + +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. +1. Generate a random value that you can use as a `nonce`. +1. Obtain an `id_token` from Apple using the Apple SDK. Make sure to also submit the SHA256 encrypted `nonce`. +1. Submit the `id_token` and `nonce` (as the `raw_id_token_nonce`) as part of the `updateRegistrationFlow` or `updateLoginFlow` + request to Ory. +1. 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 From fcba747e85d12334839ffbc7605d3230ba5045eb Mon Sep 17 00:00:00 2001 From: Jonas Hungershausen Date: Tue, 12 Sep 2023 15:04:49 +0200 Subject: [PATCH 2/3] chore: update snippet --- .../apple-social-sign-in-native.ts | 70 +++++++++++++------ 1 file changed, 49 insertions(+), 21 deletions(-) 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 index 7465bb3ba..5ffd754ca 100644 --- 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 @@ -1,34 +1,62 @@ +import { FrontendApi } from "@ory/client" import * as AppleAuthentication from "expo-apple-authentication" import * as Crypto from "expo-crypto" -import * as Random from "expo-random" - -async function signInWithApple() { - const nonce = Random.getRandomBytes(16).toString() +async function signInWithApplePayload(): Promise<{ + id_token: string + id_token_nonce: string + traits: Record +}> { const digest = await Crypto.digestStringAsync( Crypto.CryptoDigestAlgorithm.SHA256, - nonce, + Crypto.getRandomBytes(16).toString(), ) - const credential = await AppleAuthentication.signInAsync({ - requestedScopes: [ - AppleAuthentication.AppleAuthenticationScope.EMAIL, - AppleAuthentication.AppleAuthenticationScope.FULL_NAME, - ], - nonce: digest, + 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, + }, }) +} - return orySdk.updateRegistrationFlow({ - flow: flow.id, +export async function registerWithApple(sdk: FrontendApi, flowId: string) { + const payload = await signInWithApplePayload() + return sdk.updateRegistrationFlow({ + flow: flowId, updateRegistrationFlowBody: { + method: "oidc", provider: "apple", - id_token: credential.identityToken, - raw_id_token_nonce: nonce, - traits: { - name: { - first: credential.fullName?.givenName || "given name", // When developing, these values might be empty - last: credential.fullName?.familyName || "last name", // When developing, these values might be empty - }, - }, + ...payload, }, }) } From 69aaacc7a62decaa40509ba8a8d9b7c944d97bef Mon Sep 17 00:00:00 2001 From: Jonas Hungershausen Date: Tue, 12 Sep 2023 17:55:48 +0200 Subject: [PATCH 3/3] chore: cr --- docs/kratos/social-signin/30_apple.mdx | 12 ++++++------ docs/kratos/social-signin/96_native-apps.mdx | 9 ++++++++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/docs/kratos/social-signin/30_apple.mdx b/docs/kratos/social-signin/30_apple.mdx index b802824a7..895bfd876 100644 --- a/docs/kratos/social-signin/30_apple.mdx +++ b/docs/kratos/social-signin/30_apple.mdx @@ -182,17 +182,17 @@ Follow these steps to add Apple as a social sign-in provider to your project usi 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). -Since this flow does not involve a browser and redirect URL, but instead issues an `id_token`, it requires a bit more work to -integrate with Ory. +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. -1. Generate a random value that you can use as a `nonce`. -1. Obtain an `id_token` from Apple using the Apple SDK. Make sure to also submit the SHA256 encrypted `nonce`. -1. Submit the `id_token` and `nonce` (as the `raw_id_token_nonce`) as part of the `updateRegistrationFlow` or `updateLoginFlow` +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. -1. Ory will validate the `id_token` and create an identity and optionally a session (if configured). +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. 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)! :::