パスキー

Spring Security provides support for passkeys (英語) . Passkeys are a more secure method of authenticating than passwords and are built using WebAuthn [W3C] (英語) .

In order to use a passkey to authenticate, a user must first 新しい資格情報を登録します。 After the credential is registered, it can be used to authenticate by verifying an authentication assertion.

必要な依存関係

開始するには、プロジェクトに webauthn4j-core 依存関係を追加します。

これは、Spring Security の入手に従って、Spring Security のバージョンを Spring Boot または Spring Security の BOM を使用して管理していることを前提としています。

パスキーの依存関係
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
    <groupId>com.webauthn4j</groupId>
    <artifactId>webauthn4j-core</artifactId>
    <version>0.28.2.RELEASE</version>
</dependency>
depenendencies {
    implementation "org.springframework.security:spring-security-web"
    implementation "com.webauthn4j:webauthn4j-core:0.28.2.RELEASE"
}

構成

The following configuration enables passkey authentication. It provides a way to 新しい資格情報を登録する at /webauthn/register and a default log in page that allows authenticating with passkeys.

  • Java

  • Kotlin

@Bean
SecurityFilterChain filterChain(HttpSecurity http) {
	http
		// ...
		.formLogin(withDefaults())
		.webAuthn((webAuthn) -> webAuthn
			.rpName("Spring Security Relying Party")
			.rpId("example.com")
			.allowedOrigins("https://example.com")
		);
	return http.build();
}

@Bean
UserDetailsService userDetailsService() {
	UserDetails userDetails = User.withDefaultPasswordEncoder()
		.username("user")
		.password("password")
		.roles("USER")
		.build();

	return new InMemoryUserDetailsManager(userDetails);
}
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
	http {
		webAuthn {
			rpName = "Spring Security Relying Party"
			rpId = "example.com"
			allowedOrigins = setOf("https://example.com")
		}
	}
}

@Bean
open fun userDetailsService(): UserDetailsService {
	val userDetails = User.withDefaultPasswordEncoder()
		.username("user")
		.password("password")
		.roles("USER")
		.build()
	return InMemoryUserDetailsManager(userDetails)
}

新しい資格情報を登録する

パスキーを使用するには、ユーザーはまず新しい資格情報を登録する [W3C] (英語) を実行する必要があります。

新しい資格情報の登録は、次の 2 つの手順で構成されます。

  1. Requesting the Registration Options

  2. Registering the Credential

Request the Registration Options

The first step in registration of a new credential is to request the registration options. In Spring Security, a request for the registration options is typically done using JavaScript and looks like:

Spring Security provides a default registration page that can be used as a reference on how to register credentials.

Request for Registration Options
POST /webauthn/register/options
X-CSRF-TOKEN: 4bfd1575-3ad1-4d21-96c7-4ef2d9f86721

The request above will obtain the registration options for the currently authenticated user. Since the challenge is persisted (state is changed) to be compared at the time of registration, the request must be a POST and include a CSRF token.

Response for Registration Options
{
  "rp": {
    "name": "SimpleWebAuthn Example",
    "id": "example.localhost"
  },
  "user": {
    "name": "[email protected] (英語)  ",
    "id": "oWJtkJ6vJ_m5b84LB4_K7QKTCTEwLIjCh4tFMCGHO4w",
    "displayName": "[email protected] (英語)  "
  },
  "challenge": "q7lCdd3SVQxdC-v8pnRAGEn1B2M-t7ZECWPwCAmhWvc",
  "pubKeyCredParams": [
    {
      "type": "public-key",
      "alg": -8
    },
    {
      "type": "public-key",
      "alg": -7
    },
    {
      "type": "public-key",
      "alg": -257
    }
  ],
  "timeout": 300000,
  "excludeCredentials": [],
  "authenticatorSelection": {
    "residentKey": "required",
    "userVerification": "preferred"
  },
  "attestation": "direct",
  "extensions": {
    "credProps": true
  }
}

Registering the Credential

After the registration options are obtained, they are used to create the credentials that are registered. To register a new credential, the application should pass the options to navigator.credentials.create (英語) after base64url decoding the binary values such as user.idchallengeexcludeCredentials[].id.

The returned value can then be sent to the server as a JSON request. An example registration request can be found below:

Example Registration Request
POST /webauthn/register
X-CSRF-TOKEN: 4bfd1575-3ad1-4d21-96c7-4ef2d9f86721

{
  "publicKey": { (1)
    "credential": {
      "id": "dYF7EGnRFFIXkpXi9XU2wg",
      "rawId": "dYF7EGnRFFIXkpXi9XU2wg",
      "response": {
        "attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YViUy9GqwTRaMpzVDbXq1dyEAXVOxrou08k22ggRC45MKNhdAAAAALraVWanqkAfvZZFYZpVEg0AEHWBexBp0RRSF5KV4vV1NsKlAQIDJiABIVggQjmrekPGzyqtoKK9HPUH-8Z2FLpoqkklFpFPQVICQ3IiWCD6I9Jvmor685fOZOyGXqUd87tXfvJk8rxj9OhuZvUALA",
        "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiSl9RTi10SFJYRWVKYjlNcUNrWmFPLUdOVmlibXpGVGVWMk43Z0ptQUdrQSIsIm9yaWdpbiI6Imh0dHBzOi8vZXhhbXBsZS5sb2NhbGhvc3Q6ODQ0MyIsImNyb3NzT3JpZ2luIjpmYWxzZX0",
        "transports": [
          "internal",
          "hybrid"
        ]
      },
      "type": "public-key",
      "clientExtensionResults": {},
      "authenticatorAttachment": "platform"
    },
    "label": "1password" (2)
  }
}
1The result of calling navigator.credentials.create with binary values base64url encoded.
2A label that the user selects to have associated with this credential to help the user distinguish the credential.
Example Successful Registration Response
HTTP/1.1 200 OK

{
  "success": true
}

Verifying an Authentication Assertion

Verifying a credential is composed of two steps:

  1. Requesting the Verification Options

  2. Verifying the Credential

Request the Verification Options

The first step in verification of a credential is to request the verification options. In Spring Security, a request for the verification options is typically done using JavaScript and looks like:

Spring Security provides a default log in page that can be used as a reference on how to verify credentials.

Request for Verification Options
POST /webauthn/authenticate/options
X-CSRF-TOKEN: 4bfd1575-3ad1-4d21-96c7-4ef2d9f86721

The request above will obtain the verification options. Since the challenge is persisted (state is changed) to be compared at the time of authentication, the request must be a POST and include a CSRF token.

The response will contain the options for obtaining a credential with binary values such as challenge base64url encoded.

Example Response for Verification Options
{
  "challenge": "cQfdGrj9zDg3zNBkOH3WPL954FTOShVy0-CoNgSewNM",
  "timeout": 300000,
  "rpId": "example.localhost",
  "allowCredentials": [],
  "userVerification": "preferred",
  "extensions": {}
}

Verifying the Credential

After the verification options are obtained, they are used to get a credential. To get a credential, the application should pass the options to navigator.credentials.get (英語) after base64url decoding the binary values such as challenge.

The returned value of navigator.credentials.get can then be sent to the server as a JSON request. Binary values such as rawId and response.* must be base64url encoded. An example authentication request can be found below:

Example Authentication Request
POST /login/webauthn
X-CSRF-TOKEN: 4bfd1575-3ad1-4d21-96c7-4ef2d9f86721

{
  "id": "dYF7EGnRFFIXkpXi9XU2wg",
  "rawId": "dYF7EGnRFFIXkpXi9XU2wg",
  "response": {
    "authenticatorData": "y9GqwTRaMpzVDbXq1dyEAXVOxrou08k22ggRC45MKNgdAAAAAA",
    "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiRFVsRzRDbU9naWhKMG1vdXZFcE9HdUk0ZVJ6MGRRWmxUQmFtbjdHQ1FTNCIsIm9yaWdpbiI6Imh0dHBzOi8vZXhhbXBsZS5sb2NhbGhvc3Q6ODQ0MyIsImNyb3NzT3JpZ2luIjpmYWxzZX0",
    "signature": "MEYCIQCW2BcUkRCAXDmGxwMi78jknenZ7_amWrUJEYoTkweldAIhAMD0EMp1rw2GfwhdrsFIeDsL7tfOXVPwOtfqJntjAo4z",
    "userHandle": "Q3_0Xd64_HW0BlKRAJnVagJTpLKLgARCj8zjugpRnVo"
  },
  "clientExtensionResults": {},
  "authenticatorAttachment": "platform"
}
Example Successful Authentication Response
HTTP/1.1 200 OK

{
  "redirectUrl": "/", (1)
  "authenticated": true (2)
}
1The URL to redirect to
2Indicates that the user is authenticated
Example Authentication Failure Response
HTTP/1.1 401 OK