ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
パスキーの新機能
iOS、iPadOS、macOS、visionOS 26によるパスキーの強化について説明します。キーのアップデートとしては、新しいアカウント作成APIによるサインアップの簡素化、パスキーを最新の状態に保つ方法、パスキーの自動アップグレードやパスキー管理エンドポイントを使用してパスキーをアップグレードする方法、パスキーを安全にインポート/エクスポートする方法などがあります。これらの機能強化によってユーザー体験とセキュリティを向上させる方法、およびこれらのアップデートをアプリに実装し、よりスムーズで安全な認証体験を提供する方法を紹介します。このビデオの内容を十分理解できるよう、最初に、WWDC22の「Meet passkeys」を視聴してください。
関連する章
- 0:00 - Introduction
- 0:58 - The passkey journey
- 3:30 - Account creation API
- 10:48 - Keep passkeys up-to-date
- 14:41 - Automatic passkey upgrades
- 16:59 - Passkey management endpoints
- 19:12 - Importing and exporting passkeys
リソース
- ASCredentialExportManager
- ASCredentialProviderViewController
- Performing fast account creation with passkeys
関連ビデオ
WWDC24
WWDC22
-
このビデオを検索
Hey, I’m Andrew, an engineer on the Authentication Experience team. We care a lot about making sign-in simple, fast, and secure.
Passwords are the root cause of many of the problems with sign-in and account security. The industry has been working to solve these problems at their source by eliminating the use of passwords for online accounts in favor of passkeys. Passkeys fundamentally solve the security and usability problems with passwords, including eliminating phishing as we know it today, all while delivering a delightful sign-in experience.
And since you're watching this video, you’re on the team that’s making this happen. We’re working toward that important, overarching goal of replacing passwords with passkeys.
The passkey journey is the path to this goal, an industry-wide transition to authentication secured only by non-phishable factors. It involves moving through stages. Prior to passkeys, many accounts were created relying only on phishable factors, like passwords or one-time codes sent over SMS or email.
The journey began by adding a non-phishable method, like passkeys, as an additional sign-in method. This is where much of the industry is today. The goal is for all accounts to reach a state of having no phishable factors, and passkeys are how the industry will get everyone there.
There is fantastic momentum across the board with growing adoption from users and increasing support across services. In 2025, the FIDO Alliance, the standards body behind passkeys, did a study and found 69% of people surveyed have at least one passkey.
Google published that people signing in with passkeys are four times more successful at getting into their accounts than those using passwords. And TikTok has observed a stunning 97% sign-in success rate for people using passkeys. This kind of sign-in success with passkeys is common across the industry and unheard of with passwords.
This growing and successful adoption shows clear progress on the passkey journey. iOS, iPadOS, macOS, and visionOS 26 build on passkeys with new enhancements that make it even easier for people to discover, create, and use them across your apps and websites.
I’ll cover five key updates. First, the new account creation API. It’s the fastest, easiest way to create a new account, and it gives you a passkey from the start.
Then, I’ll cover keeping passkeys up to date, showing you how to sync account changes with credential managers.
Next, automatic passkey upgrades, providing a seamless path for adding passkeys to existing password-based accounts. After that, passkey management endpoints, a way to showcase your service’s passkey adoption directly within credential managers.
And finally, importing and exporting passkeys, an important ecosystem advancement that gives people more flexibility and control over their passkeys.
First up, the new account creation API. Unlike traditional password-based sign-ups, setting up a passkey isn’t a lengthy or complex process. A quick, simple sign-up feels effortless. Passkeys don’t rely on someone creating a complex password and trying to recall it.
Passkeys make sign-in fast and effortless, and they’re resistant to phishing. With the account creation API and the latest OS releases, sign-ups can be a delightful, fast, and a fundamentally more secure experience.
I’ll show you on my iPhone in my demo app, Shiny. It promises one cute picture a day.
Like anyone encountering a new app, the first step is signing up. This is the traditional way before adopting the account creation API.
I’ll start by creating a new account with my email.
I'm immediately asked to fill out a form, starting with the email. Fortunately, autofill does help me out here.
That's the email done.
Now I’ll do my first name and my last name.
And the dreaded password, of course. I’ll go with the best case scenario and use the automatic strong password.
And finally, after all that tap continue and I’m signed up. There’s the cute picture. How adorable.
Now I’ll try this again after adopting the account creation API. In a similar fashion, I’ll create a new account with my email.
But notice, instead of the multi-step form, the system presents a sheet that shows exactly what Shiny is requesting. In this case, my name and email and a passkey that will be saved in the Passwords app. And the best part, it’s all prefilled for me.
This is the new streamlined flow powered by the account creation API.
The info I share is editable. When I tap a field, I get a picker that offers suggestions and I can switch between them or manually enter something. I’m happy with the defaults here. So I’ll tap continue, Face ID authenticates me, and that’s it. I'm signed up and into Shiny. Much faster, simpler, and more secure.
And signing in is just as effortless.
If I later grab my iPad and launch Shiny for the first time on it, I immediately get a sign-in prompt because the app is set up to check for existing credentials right at launch. And the passkey I created earlier is available across all my devices.
Tap continue, Face ID, and I’m in. So easy! This experience is available on apps on iOS, iPadOS, macOS, and visionOS, and it works with the Passwords app and third-party credential managers.
Now, I’ll go through the implementation that powers that sign-up experience.
Here’s a function showing the core steps for account creation. Start by initializing the new account creation provider. Next, with that provider, create a passkey registration request. This method is where the key details are specified. The first parameter is accepted contact identifiers. This tells the system what kind of account identifiers your app supports. Your app receives only the one chosen identifier for creating the account, which is also used as a passkey’s user name label.
Should request name determines whether to request the user’s name. You should only set this to true if it’s necessary for account creation.
The next three pertain to the passkey that’ll be created. These match standard passkey registration, so provide the same data as you would for that request.
The relying party to register the passkey with, generally a domain name, a single use challenge to be signed by the created passkey, fetched from your server, and the user ID is a stable, unique identifier you create for the account.
Next, get an instance to ASAuthorizationController. In a SwiftUI view, you can access that using the Environment property wrapper.
Then, perform the request. When someone successfully completes the sign-up flow, you’ll get back an ASAuthorizationResult. Unwrap it and you’ll now have access to a contact identifier, the name if requested, as well as a passkey object. Go ahead and create a new account on your server and sign them in.
And that's really the core of adoption.
If something goes wrong, the perform method will throw an error that you can handle to make the experience great in all cases. Alongside the standard AuthenticationServices errors, there are three errors to handle especially relevant to this API. The first is deviceNotConfiguredForPasskeyCreation This means the device isn’t in a state to create a passkey right now, such as if it doesn’t have a passcode set. You can present your standard sign-up form knowing they aren’t eligible for passkey creation at this time. When tapping the use email or phone number button in Shiny, this error is handled by presenting a custom sign-up form.
The next is canceled. This error is thrown if someone dismisses the sign-up sheet without completing it. In this case, you can present your standard sign-up form. When the system sheet is dismissed in Shiny, this error is handled by presenting a custom sign-up form. And the last is preferSignInWithApple. You’ll only ever get this error back if your app supports Sign in with Apple. It’s designed to help prevent accidental duplicate accounts. If someone initiates the account creation flow but already has an account for your service using Sign in with Apple, the system reminds them by showing an alert like this.
They’re given a choice: use their existing Sign in with Apple account or continue creating a new account. If they use their existing account, that’s when this error is thrown. So when you receive this error, the best practice is to initiate a Sign in with Apple request, which presents the Sign in with Apple sheet. That respects their choice and gets them signed into their existing account without extra taps.
That’s all for errors. Another best practice is to offer any existing accounts immediately on app launch, helping guide people to get signed back into your app. Recall from the iPad demo, I was immediately prompted to sign in with my passkey, without needing to think about whether I had an existing account or how I signed in last time.
This happened because Shiny used a combined sign in request with the prefer immediately available credentials option set. If someone has any available account credentials on their device, whether a passkey or password from any credential manager app or an account using Sign in with Apple, they’ll be offered for sign in. And if there aren’t any credentials immediately available, no UI is shown, leaving your sign-in screen uninterrupted.
To learn about adopting this, watch the “Meet Passkeys” video from WWDC22.
And that’s the account creation API, a brand new feature that makes sign-up effortless and secure.
Once an account is created, ensuring its passkey information stays accurate in credential managers helps maintain a smooth experience. This brings me to the next topic, keeping passkeys up to date.
Accounts aren't static. People update their information, like user names or email addresses, and can revoke passkeys. If credential managers show outdated information during sign-in, it can cause confusion and slow down or prevent someone from signing into your service. There's a new collection of API that enable your app or website to signal to credential managers about changes to an account. Adopting these signal APIs keep entries accurate, ensuring sign-ins are fast, reliable, and just work.
Here’s how this works in Shiny. Imagine I’ve recently graduated from university and want to switch my account for my old student email to my personal one.
I go into the app settings, tap email, and enter my new email.
Then I save.
Immediately after, my credential manager, The Passwords app notifies me that the user name changed.
And if I tap on it, Passwords authenticates me and then presents the details for the account.
Notably, the user name reflects the new personal email. The credential manager now matches what's on the app's backend.
How neat is that! This is enabled by a new API available for apps and the web. For apps, the new ASCredentialUpdater class provides methods to report specific credential changes. For the web, equivalent methods are part of the WebAuthn standard supported in Safari 19 and other browsers.
The first signal is for changing the user name. Like in the demo, if someone updates their user name, email address, or whatever label is shown for the account, use the reportPublicKeyCredentialUpdate method to inform the credential manager. This ensures that the label displayed during sign-in is always up to date, preventing any confusion. As a reminder, user names on passkeys are a local only label. They are not returned to the server during authentication.
For the web, use the signalCurrentUserDetails method on PublicKeyCredential to accomplish the same thing.
Next, revoking a passkey.
Many apps and websites have passkey management pages where someone can revoke an individual passkey or create a new one.
Anytime a passkey gets revoked, call the reportAllAcceptedPublicKeyCredentials method, specifying the credential IDs that remain valid.
When you do this, credential managers will delete any passkeys they have that aren’t in the set of what you provided. That will prevent any invalid passkey from being offered when someone signs in. You can also call this periodically as a health check for an account. If something has changed, this will help make sure credential managers are in the loop.
And you can do the same on the web too, using the signalAllAcceptedCredentials method.
Ultimately, the most secure accounts are the ones that don’t have a password at all. Accounts created with the new account creation API never have a password, so they’re secure from day one.
For existing accounts, you can now inform credential managers when a password is no longer needed. The reportUnusedPasswordCredential API signals that an account has taken that final step on the passkey journey, and is password-free.
And of course, all credential managers can participate in handling these signals. There is new API to adopt to listen to these update signals and keep managed credentials up to date.
For more information, check out developer documentation for the report methods on ASCredentialProviderViewController.
So these new API help maintain credential accuracy.
Next, I’ll focus on encouraging the adoption of passkeys, especially for people still signing in with passwords.
Automatic passkey upgrades provide a seamless path for adding passkeys to existing password-based accounts.
Your app or website can create one right after a password sign-in with zero friction. I'll show you. Back in Shiny, imagine I still use the password and want to sign in.
I’ll autofill my user name and password and tap sign in, and I'm in.
But because Shiny adopted automatic passkey upgrades, here's what happens now. I got a notification that a passkey has been created. No upsell screens or interruptions, just a seamless transition unlocking a simpler and more secure sign-in experience.
It’s important to note that the password I just used still works. This process simply adds the passkey as a secure way to sign in.
Drawn in code, this is an automatic passkey upgrade.
After someone signs in using a password, you have access to their account details. Start by ensuring the account doesn't already have a passkey.
If not, create a passkey registration request as you would when creating a passkey.
Setting the request style to conditional is what enables the automatic passkey upgrade. With that set, after you perform the request, the system and credential manager run checks in the background. These include verifying if a credential manager is available, if the password for this account was just used, and if the device is set up for passkeys. If all the preconditions are satisfied, the system creates a passkey and shows a notification, and your app receives that passkey object to save, all of which happens without interrupting or blocking the user.
If any of the preconditions weren’t satisfied, the call fails silently. The system will not show any UI and no specific error handling is needed.
Your app should attempt the upgrade on every password sign-in, if the user doesn’t already have a passkey.
There's also an equivalent web API. To learn even more about automatic passkey upgrades, watch the WWDC24 video “Streamline sign-in with passkey upgrades and credential managers”.
Automatic upgrades offer that ideal zero-friction path.
To complement this, adopting the well-known URL for passkey management endpoints, lets you surface direct links from credential managers to your passkey management pages. It’s a great way to highlight your passkey adoption. I'll show you. Here, I’m in the Passwords app, reviewing a saved entry for Shiny that still uses a password.
Notice the new section letting me know there's a passkey upgrade available. Tapping the add passkey button opens the website directly to the page specified for passkey enrollment.
Implementing this standard creates a direct link from credential managers right to your passkey enrollment pages. This enables another way for people to upgrade right from their credential manager. And because it's built on a standard, this works across all participating credential managers.
To support this, survey JSON response at the well known passkey end points path on your server.
The response must be directly from this path, not through a redirect.
Serve this response with a 200 OK status code and a content type header of application JSON.
The response content is a JSON dictionary pointing to relevant pages on your website.
Enroll is the URL where someone adds a passkey to their account. This is what the add passkey button in the Passwords app links to.
Manage is the URL where someone manages their existing passkeys, analogous to a changed password page. It’s where someone can revoke existing passkeys or add a new one.
All fields are optional, but it’s best to include both.
It’s important that these URLs correctly handle people that arrive without an authenticated session. If sign-in is necessary, first authenticate, then redirect back to the originally requested URL.
Also, ensure that this page is accessible and served to all user agents. It’s not just web browsers that’ll request this. Apps like credential managers will too.
By implementing the well-known URL for passkey management endpoints, you provide credential managers a hint to guide people toward adopting passkeys for your service.
Next, I’ll cover importing and exporting passkeys.
People own their credentials and should have the flexibility to manage them where they choose. That's why I'm happy to let you know, passkeys can now be transferred securely between participating credential manager apps on iOS, iPadOS, macOS, and visionOS 26. This gives people more control over their data and the choice of which credential manager they use.
This new process is fundamentally different and more secure than traditional credential export methods, which often involve exporting an unencrypted CSV or JSON file, then manually importing it into another app. The transfer process is user initiated, occurs directly between participating credential manager apps and is secured by local authentication like Face ID.
This transfer uses a data schema that was built in collaboration with the members of the FIDO Alliance. It standardizes the data format for passkeys, passwords, verification codes, and more data types.
The system provides a secure mechanism to move the data between apps. No insecure files are created on disk, eliminating the risk of credential leaks from exported files. It’s a modern, secure way to move credentials.
Apps and websites don’t need to do anything to accommodate credential transfer because the process happens directly between credential managers. Existing passkeys remain unchanged and will continue to work seamlessly.
For credential manager apps looking to support credential transfer, refer to the developer documentation for ASCredentialExportManager and ASCredentialImportManager.
Passkeys continue to advance, offering stronger security with simpler experiences. The new OS releases make them even easier to create, use, and discover. We’re on this journey together to a passwordless future, and your adoption of these features is key to making that a reality for everyone. So to help you continue to deliver the best authentication experience, here are the next steps. First, adopt the account creation API for secure and quick onboarding.
Next, keep passkeys up to date using the Signal API to keep credential managers informed about account changes like updated user names or passkey removals. Then enable automatic passkey upgrades for the most seamless transition from passwords. And finally, make passkey upgrades discoverable from credential managers by serving the passkey management endpoints from your website. That's what's new in passkeys. Thanks for watching.
-
-
6:33 - Account creation
// Account creation @Environment(\.authorizationController) var authorizationController func performPasskeySignUp() async throws { let provider = ASAuthorizationAccountCreationProvider() let request = provider.createPlatformPublicKeyCredentialRegistrationRequest( acceptedContactIdentifiers: [.email, .phoneNumber], shouldRequestName: true, relyingPartyIdentifier: "example.com", challenge: try await fetchChallenge(), userID: try await fetchUserID() ) do { let result = try await authorizationController.performRequest(request) if case .passkeyAccountCreation(let account) = result { // Register new account on backend } } catch ASAuthorizationError .deviceNotConfiguredForPasskeyCreation { showPasswordSignUpForm = true } catch ASAuthorizationError.canceled { showPasswordSignUpForm = true } catch ASAuthorizationError.preferSignInWithApple { await performSignInWithApple() } catch { ... } }
-
12:30 - Changing the user name
// Changing the user name try await ASCredentialUpdater() .reportPublicKeyCredentialUpdate( relyingPartyIdentifier: "example.com", userHandle: userHandle, newName: "andrew@example.com" )
-
12:58 - Changing the user name
// Changing the user name await PublicKeyCredential.signalCurrentUserDetails({ rpId: "example.com", userId: userHandle, name: "andrew@example.com", displayName: "andrew@example.com" });
-
13:07 - Revoking a passkey
// Revoking a passkey try await ASCredentialUpdater() .reportAllAcceptedPublicKeyCredentials( relyingPartyIdentifier: "example.com", userHandle: userHandle, acceptedCredentialIDs: acceptedCredentialIDs )
-
13:46 - Revoking a passkey
// Revoking a passkey await PublicKeyCredential.signalAllAcceptedCredentials({ rpId: "example.com", userId: userHandle, allAcceptedCredentalIds: acceptedCredentialIds });
-
14:04 - Removing a password
// Removing a password try await ASCredentialUpdater() .reportUnusedPasswordCredential( domain: "example.com", username: "andrew@example.com" )
-
15:36 - Automatic passkey upgrade
// Automatic passkey upgrade func signIn() async throws { let accountDetails = try await signInWithPassword() guard !accountDetails.hasPasskey else { return } let provider = ASAuthorizationPlatformPublicKeyCredentialProvider( relyingPartyIdentifier: "example.com") let request = provider.createCredentialRegistrationRequest( challenge: try await fetchChallenge(), name: accountDetails.userName, userID: accountDetails.userID, requestStyle: .conditional ) do { let passkey = try await authorizationController.performRequest(request) // Save new passkey to the backend } catch { ... } }
-