From 0966b1e5a3e0855296b53cfe1bf616ebe25f5bf0 Mon Sep 17 00:00:00 2001 From: Victor Stanchev Date: Fri, 28 Mar 2025 05:55:02 +0200 Subject: [PATCH 1/7] Update `options` type to object in SignInStart and SignUpStart schemas --- docs/openapi/components/schemas/SignInStartResponse.yaml | 7 +++++-- docs/openapi/components/schemas/SignUpStartResponse.yaml | 5 ++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/openapi/components/schemas/SignInStartResponse.yaml b/docs/openapi/components/schemas/SignInStartResponse.yaml index 71fb8a9..888b398 100644 --- a/docs/openapi/components/schemas/SignInStartResponse.yaml +++ b/docs/openapi/components/schemas/SignInStartResponse.yaml @@ -8,8 +8,11 @@ properties: type: boolean description: Indicates whether or not this account exists options: - type: string + type: object description: > - JSON string representing the options argument that should be passed to `navigator.credentials.get(options)` if + JSON object representing the options argument that should be passed to `navigator.credentials.get(options)` if `accountExists` is `true` or to `navigator.credentials.create(options)` if `accountExists` is `false`. x-field-extra-annotation: '@com.fasterxml.jackson.annotation.JsonRawValue' + properties: + publicKey: + type: object diff --git a/docs/openapi/components/schemas/SignUpStartResponse.yaml b/docs/openapi/components/schemas/SignUpStartResponse.yaml index e9144c7..c6342b1 100644 --- a/docs/openapi/components/schemas/SignUpStartResponse.yaml +++ b/docs/openapi/components/schemas/SignUpStartResponse.yaml @@ -7,6 +7,9 @@ properties: type: string description: Unique identifier for the sign-up request. options: - type: string + type: object description: Options to pass to `navigator.credentials.create()` x-field-extra-annotation: '@com.fasterxml.jackson.annotation.JsonRawValue' + properties: + publicKey: + type: object From f692e29218ad008d0c60be964b8bb22f2f65ba83 Mon Sep 17 00:00:00 2001 From: Victor Stanchev Date: Sat, 29 Mar 2025 06:41:05 +0200 Subject: [PATCH 2/7] Add authentication providers for API key and application ID --- ...plicationApiKeyAuthenticationProvider.java | 63 +++++++++++++++++ .../ApplicationIdAuthenticationProvider.java | 69 +++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 src/main/java/com/helioauth/passkeys/api/auth/ApplicationApiKeyAuthenticationProvider.java create mode 100644 src/main/java/com/helioauth/passkeys/api/auth/ApplicationIdAuthenticationProvider.java diff --git a/src/main/java/com/helioauth/passkeys/api/auth/ApplicationApiKeyAuthenticationProvider.java b/src/main/java/com/helioauth/passkeys/api/auth/ApplicationApiKeyAuthenticationProvider.java new file mode 100644 index 0000000..90ce743 --- /dev/null +++ b/src/main/java/com/helioauth/passkeys/api/auth/ApplicationApiKeyAuthenticationProvider.java @@ -0,0 +1,63 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.helioauth.passkeys.api.auth; + +import com.helioauth.passkeys.api.domain.ClientApplication; +import com.helioauth.passkeys.api.domain.ClientApplicationRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class ApplicationApiKeyAuthenticationProvider implements AuthenticationProvider { + + private final ClientApplicationRepository clientApplicationRepository; + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + String apiKeyHeader = (String) authentication.getPrincipal(); + + if (apiKeyHeader == null || apiKeyHeader.isBlank()) { + throw new BadCredentialsException("Application API key header is missing or empty"); + } + + ClientApplication clientApp = clientApplicationRepository.findByApiKey(apiKeyHeader) + .orElseThrow(() -> new BadCredentialsException("Invalid api key")); + + PreAuthenticatedAuthenticationToken authenticatedToken = new PreAuthenticatedAuthenticationToken( + clientApp.getId(), + clientApp, + List.of(new SimpleGrantedAuthority("ROLE_APPLICATION")) + ); + authenticatedToken.setDetails(clientApp); + + return authenticatedToken; + } + + @Override + public boolean supports(Class authentication) { + return PreAuthenticatedAuthenticationToken.class.isAssignableFrom(authentication); + } +} \ No newline at end of file diff --git a/src/main/java/com/helioauth/passkeys/api/auth/ApplicationIdAuthenticationProvider.java b/src/main/java/com/helioauth/passkeys/api/auth/ApplicationIdAuthenticationProvider.java new file mode 100644 index 0000000..0b595b6 --- /dev/null +++ b/src/main/java/com/helioauth/passkeys/api/auth/ApplicationIdAuthenticationProvider.java @@ -0,0 +1,69 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.helioauth.passkeys.api.auth; + +import com.helioauth.passkeys.api.domain.ClientApplication; +import com.helioauth.passkeys.api.domain.ClientApplicationRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.UUID; + +@Service +@RequiredArgsConstructor +public class ApplicationIdAuthenticationProvider implements AuthenticationProvider { + + private final ClientApplicationRepository clientApplicationRepository; + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + String appIdHeader = (String) authentication.getPrincipal(); + + if (appIdHeader == null || appIdHeader.isBlank()) { + throw new BadCredentialsException("Application ID header is missing or empty"); + } + + try { + UUID appId = UUID.fromString(appIdHeader); + ClientApplication clientApp = clientApplicationRepository.findById(appId) + .orElseThrow(() -> new BadCredentialsException("Invalid application ID")); + + PreAuthenticatedAuthenticationToken authenticatedToken = new PreAuthenticatedAuthenticationToken( + clientApp.getId(), + clientApp, + List.of(new SimpleGrantedAuthority("ROLE_FRONTEND_APPLICATION")) + ); + authenticatedToken.setDetails(clientApp); + + return authenticatedToken; + } catch (IllegalArgumentException e) { + throw new BadCredentialsException("Invalid application ID format"); + } + } + + @Override + public boolean supports(Class authentication) { + return PreAuthenticatedAuthenticationToken.class.isAssignableFrom(authentication); + } +} \ No newline at end of file From 5021589f0b41a00c8eb95f816a1e472c8b151c01 Mon Sep 17 00:00:00 2001 From: Victor Stanchev Date: Sat, 29 Mar 2025 06:43:51 +0200 Subject: [PATCH 3/7] Add app ID and API key headers to configuration files --- src/main/resources/application-test.yaml | 5 +++++ src/main/resources/application.yaml | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/main/resources/application-test.yaml b/src/main/resources/application-test.yaml index ba54951..33aa1cd 100644 --- a/src/main/resources/application-test.yaml +++ b/src/main/resources/application-test.yaml @@ -7,3 +7,8 @@ admin: auth: api-key: testapikey header-name: X-Api-Key + +app: + auth: + app-id-header: X-App-Id + api-key-header: X-Api-Key diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 6c99516..44fbe57 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -12,3 +12,8 @@ relying-party: display-name: HelioAuth Passkeys API hostname: localhost allow-origin-port: false + +app: + auth: + app-id-header: X-App-Id + api-key-header: X-Api-Key From 2d3f4507faef88c7b3cd2063787528a9b5c1f440 Mon Sep 17 00:00:00 2001 From: Victor Stanchev Date: Sat, 29 Mar 2025 06:44:05 +0200 Subject: [PATCH 4/7] Add app ID and API key headers to configuration files --- .../passkeys/api/domain/ClientApplicationRepository.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/helioauth/passkeys/api/domain/ClientApplicationRepository.java b/src/main/java/com/helioauth/passkeys/api/domain/ClientApplicationRepository.java index 0a6b671..1576fc9 100644 --- a/src/main/java/com/helioauth/passkeys/api/domain/ClientApplicationRepository.java +++ b/src/main/java/com/helioauth/passkeys/api/domain/ClientApplicationRepository.java @@ -2,7 +2,9 @@ import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; import java.util.UUID; public interface ClientApplicationRepository extends JpaRepository { + Optional findByApiKey(String s); } \ No newline at end of file From 2073ce28e4dc4060610e0c1fb4bda62972fd026e Mon Sep 17 00:00:00 2001 From: Victor Stanchev Date: Sat, 29 Mar 2025 06:44:56 +0200 Subject: [PATCH 5/7] Refactor authentication to support admin and application roles --- ...va => AdminApiAuthenticationProvider.java} | 2 +- .../api/config/WebSecurityConfig.java | 57 ++++++++-- .../controller/CredentialsControllerTest.java | 106 +++++++++++++++--- 3 files changed, 137 insertions(+), 28 deletions(-) rename src/main/java/com/helioauth/passkeys/api/auth/{RequestHeaderAuthenticationProvider.java => AdminApiAuthenticationProvider.java} (96%) diff --git a/src/main/java/com/helioauth/passkeys/api/auth/RequestHeaderAuthenticationProvider.java b/src/main/java/com/helioauth/passkeys/api/auth/AdminApiAuthenticationProvider.java similarity index 96% rename from src/main/java/com/helioauth/passkeys/api/auth/RequestHeaderAuthenticationProvider.java rename to src/main/java/com/helioauth/passkeys/api/auth/AdminApiAuthenticationProvider.java index 8224aa5..7ca6637 100644 --- a/src/main/java/com/helioauth/passkeys/api/auth/RequestHeaderAuthenticationProvider.java +++ b/src/main/java/com/helioauth/passkeys/api/auth/AdminApiAuthenticationProvider.java @@ -33,7 +33,7 @@ */ @Service @RequiredArgsConstructor -public class RequestHeaderAuthenticationProvider implements AuthenticationProvider { +public class AdminApiAuthenticationProvider implements AuthenticationProvider { private final AdminConfigProperties adminConfigProperties; diff --git a/src/main/java/com/helioauth/passkeys/api/config/WebSecurityConfig.java b/src/main/java/com/helioauth/passkeys/api/config/WebSecurityConfig.java index c984286..94cce3b 100644 --- a/src/main/java/com/helioauth/passkeys/api/config/WebSecurityConfig.java +++ b/src/main/java/com/helioauth/passkeys/api/config/WebSecurityConfig.java @@ -16,9 +16,12 @@ package com.helioauth.passkeys.api.config; -import com.helioauth.passkeys.api.auth.RequestHeaderAuthenticationProvider; +import com.helioauth.passkeys.api.auth.AdminApiAuthenticationProvider; +import com.helioauth.passkeys.api.auth.ApplicationApiKeyAuthenticationProvider; +import com.helioauth.passkeys.api.auth.ApplicationIdAuthenticationProvider; import com.helioauth.passkeys.api.config.properties.AdminConfigProperties; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; @@ -43,19 +46,27 @@ @EnableWebSecurity @RequiredArgsConstructor public class WebSecurityConfig { - private final RequestHeaderAuthenticationProvider requestHeaderAuthenticationProvider; + private final AdminApiAuthenticationProvider adminApiAuthenticationProvider; + private final ApplicationIdAuthenticationProvider applicationIdAuthenticationProvider; + private final ApplicationApiKeyAuthenticationProvider applicationApiKeyAuthenticationProvider; @Bean public SecurityFilterChain filterChain(HttpSecurity http, - RequestHeaderAuthenticationFilter requestHeaderAuthenticationFilter) throws Exception { + RequestHeaderAuthenticationFilter adminAuthFilter, + RequestHeaderAuthenticationFilter applicationIdAuthFilter, + RequestHeaderAuthenticationFilter applicationApiKeyAuthFilter) throws Exception { http .cors(Customizer.withDefaults()) .csrf(AbstractHttpConfigurer::disable) .sessionManagement(config -> config.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) - .addFilterAfter(requestHeaderAuthenticationFilter, HeaderWriterFilter.class) + .addFilterAfter(adminAuthFilter, HeaderWriterFilter.class) + .addFilterAfter(applicationIdAuthFilter, HeaderWriterFilter.class) + .addFilterAfter(applicationApiKeyAuthFilter, HeaderWriterFilter.class) .authorizeHttpRequests(registry -> registry .requestMatchers("/admin/**").hasRole("ADMIN") + .requestMatchers("/v1/signup/start").hasRole("FRONTEND_APPLICATION") + .requestMatchers("/v1/signup/finish").hasRole("APPLICATION") .anyRequest().permitAll() ) .exceptionHandling(config -> config @@ -66,19 +77,47 @@ public SecurityFilterChain filterChain(HttpSecurity http, } @Bean - public RequestHeaderAuthenticationFilter requestHeaderAuthenticationFilter(AuthenticationManager authenticationManager, - AdminConfigProperties adminConfigProperties) { + public RequestHeaderAuthenticationFilter adminAuthFilter(AdminConfigProperties adminConfigProperties) { RequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter(); filter.setPrincipalRequestHeader(adminConfigProperties.getAuth().getHeaderName()); filter.setExceptionIfHeaderMissing(false); filter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/admin/**")); - filter.setAuthenticationManager(authenticationManager); + filter.setAuthenticationManager(adminAuthenticationManager()); return filter; } @Bean - protected AuthenticationManager authenticationManager() { - return new ProviderManager(List.of(requestHeaderAuthenticationProvider)); + public RequestHeaderAuthenticationFilter applicationIdAuthFilter(@Value("${app.auth.app-id-header}") String authHeader) { + RequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter(); + filter.setPrincipalRequestHeader(authHeader); + filter.setExceptionIfHeaderMissing(false); + filter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/v1/signup/start")); + filter.setAuthenticationManager(appIdAuthenticationManager()); + + return filter; + } + + @Bean + public RequestHeaderAuthenticationFilter applicationApiKeyAuthFilter(@Value("${app.auth.api-key-header}") String authHeader) { + RequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter(); + filter.setPrincipalRequestHeader(authHeader); + filter.setExceptionIfHeaderMissing(false); + filter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/v1/signup/finish")); + filter.setAuthenticationManager(appApiKeyAuthenticationManager()); + + return filter; + } + + protected AuthenticationManager adminAuthenticationManager() { + return new ProviderManager(List.of(adminApiAuthenticationProvider)); + } + + protected AuthenticationManager appIdAuthenticationManager() { + return new ProviderManager(List.of(applicationIdAuthenticationProvider)); + } + + protected AuthenticationManager appApiKeyAuthenticationManager() { + return new ProviderManager(List.of(applicationApiKeyAuthenticationProvider)); } } diff --git a/src/test/java/com/helioauth/passkeys/api/controller/CredentialsControllerTest.java b/src/test/java/com/helioauth/passkeys/api/controller/CredentialsControllerTest.java index e1d29b1..3cd38e7 100644 --- a/src/test/java/com/helioauth/passkeys/api/controller/CredentialsControllerTest.java +++ b/src/test/java/com/helioauth/passkeys/api/controller/CredentialsControllerTest.java @@ -16,16 +16,22 @@ package com.helioauth.passkeys.api.controller; +import com.helioauth.passkeys.api.domain.ClientApplication; +import com.helioauth.passkeys.api.domain.ClientApplicationRepository; import com.helioauth.passkeys.api.domain.User; import com.helioauth.passkeys.api.domain.UserCredentialRepository; import com.helioauth.passkeys.api.domain.UserRepository; import com.helioauth.passkeys.api.generated.models.SignInStartRequest; +import com.helioauth.passkeys.api.generated.models.SignUpFinishRequest; import com.helioauth.passkeys.api.generated.models.SignUpStartRequest; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.HttpStatus; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; @@ -35,6 +41,11 @@ import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper; +import java.time.Instant; +import java.util.Optional; +import java.util.UUID; + +import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -43,11 +54,26 @@ @ActiveProfiles("test") @Testcontainers class CredentialsControllerTest { + public static final String X_APP_ID = "X-App-Id"; + public static final String X_API_KEY = "X-Api-Key"; - @Container - static final PostgreSQLContainer postgresql = new PostgreSQLContainer<>("postgres:16-alpine"); public static final String PATH_SIGNUP_START = "/v1/signup/start"; + public static final String PATH_SIGNUP_FINISH = "/v1/signup/finish"; + public static final String PATH_SIGNIN_START = "/v1/signin/start"; + public static final String PATH_SIGNIN_FINISH = "/v1/signin/finish"; + + public static final UUID TEST_APP_ID = UUID.randomUUID(); + public static final ClientApplication TEST_APP = ClientApplication.builder() + .id(TEST_APP_ID) + .name("test") + .apiKey("testapikey") + .createdAt(Instant.now()) + .updatedAt(Instant.now()) + .build(); + + @Container + static final PostgreSQLContainer postgresql = new PostgreSQLContainer<>("postgres:16-alpine"); @DynamicPropertySource static void properties(DynamicPropertyRegistry registry) { @@ -56,6 +82,9 @@ static void properties(DynamicPropertyRegistry registry) { registry.add("spring.datasource.username", postgresql::getUsername); registry.add("spring.datasource.password", postgresql::getPassword); } + + @MockBean + ClientApplicationRepository clientApplicationRepository; @Autowired UserRepository userRepository; @@ -63,16 +92,25 @@ static void properties(DynamicPropertyRegistry registry) { @Autowired UserCredentialRepository userCredentialRepository; + @Autowired + MockMvc mockMvc; + + ObjectMapper objectMapper = new ObjectMapper(); + + @BeforeEach + void setUp() { + when(clientApplicationRepository.findById(TEST_APP_ID)) + .thenReturn(Optional.of(TEST_APP)); + + when(clientApplicationRepository.findByApiKey(TEST_APP.getApiKey())) + .thenReturn(Optional.of(TEST_APP)); + } + @AfterEach void tearDown() { userRepository.deleteAll(); userCredentialRepository.deleteAll(); } - - @Autowired - MockMvc mockMvc; - - ObjectMapper objectMapper = new ObjectMapper(); @Test void postSignUpStart() throws Exception { @@ -81,35 +119,67 @@ void postSignUpStart() throws Exception { String requestJson = objectMapper.writeValueAsString(request); mockMvc.perform(post(PATH_SIGNUP_START) + .header(X_APP_ID, TEST_APP_ID.toString()) .contentType("application/json") .content(requestJson) ).andExpect(status().isOk()); } - + @Test - void postSignInStart() throws Exception { - SignInStartRequest request = new SignInStartRequest("test"); + void postSignUpStart_existingUser() throws Exception { + userRepository.save(User.builder() + .name("test") + .displayName("test") + .build()); + + SignUpStartRequest request = new SignUpStartRequest("test"); String requestJson = objectMapper.writeValueAsString(request); - mockMvc.perform(post(PATH_SIGNIN_START) + mockMvc.perform(post(PATH_SIGNUP_START) + .header(X_APP_ID, TEST_APP_ID.toString()) .contentType("application/json") .content(requestJson) ).andExpect(status().isOk()); } @Test - void postSignUpStart_existingUser() throws Exception { - userRepository.save(User.builder() - .name("test") - .displayName("test") - .build()); + void postSignUpStart_nonExistingApp() throws Exception { + mockMvc.perform(post(PATH_SIGNUP_START) + .header(X_APP_ID, UUID.randomUUID().toString()) + .contentType("application/json") + .content("{}") + ).andExpect(status().isUnauthorized()); + } - SignUpStartRequest request = new SignUpStartRequest("test"); + @Test + void postSignUpFinish() throws Exception { + SignUpFinishRequest request = new SignUpFinishRequest("requestId", "publicKeyCredential"); + String requestJson = objectMapper.writeValueAsString(request); + + mockMvc.perform(post(PATH_SIGNUP_FINISH) + .header(X_API_KEY, TEST_APP.getApiKey()) + .contentType("application/json") + .content(requestJson) + ).andExpect(status().is(HttpStatus.BAD_REQUEST.value())); + } + + @Test + void postSignUpFinish_nonExistingApp() throws Exception { + mockMvc.perform(post(PATH_SIGNUP_FINISH) + .header(X_API_KEY, "invalid api key") + .contentType("application/json") + .content("{}") + ).andExpect(status().isUnauthorized()); + } + + @Test + void postSignInStart() throws Exception { + SignInStartRequest request = new SignInStartRequest("test"); String requestJson = objectMapper.writeValueAsString(request); - mockMvc.perform(post(PATH_SIGNUP_START) + mockMvc.perform(post(PATH_SIGNIN_START) .contentType("application/json") .content(requestJson) ).andExpect(status().isOk()); From a199fe5f8f50185e01a2cbcd50c3526c6ee435d2 Mon Sep 17 00:00:00 2001 From: Victor Stanchev Date: Sat, 29 Mar 2025 06:46:57 +0200 Subject: [PATCH 6/7] Add security schemes to OpenAPI specs for app-id and api-key --- docs/openapi/openapi.yaml | 8 ++++++++ docs/openapi/paths/v1_signin_finish.yaml | 2 ++ docs/openapi/paths/v1_signin_start.yaml | 2 ++ docs/openapi/paths/v1_signup_finish.yaml | 2 ++ docs/openapi/paths/v1_signup_start.yaml | 2 ++ 5 files changed, 16 insertions(+) diff --git a/docs/openapi/openapi.yaml b/docs/openapi/openapi.yaml index 9edc64c..3eb9c5d 100644 --- a/docs/openapi/openapi.yaml +++ b/docs/openapi/openapi.yaml @@ -77,3 +77,11 @@ components: type: apiKey name: X-Api-Key in: header + app-id: + type: apiKey + name: X-App-Id + in: header + app-api-key: + type: apiKey + name: X-Api-Key + in: header diff --git a/docs/openapi/paths/v1_signin_finish.yaml b/docs/openapi/paths/v1_signin_finish.yaml index 17bbae3..1d570b9 100644 --- a/docs/openapi/paths/v1_signin_finish.yaml +++ b/docs/openapi/paths/v1_signin_finish.yaml @@ -17,3 +17,5 @@ post: application/json: schema: $ref: ../components/schemas/SignInFinishResponse.yaml + security: + - app-api-key: [] diff --git a/docs/openapi/paths/v1_signin_start.yaml b/docs/openapi/paths/v1_signin_start.yaml index 1263a63..834bc67 100644 --- a/docs/openapi/paths/v1_signin_start.yaml +++ b/docs/openapi/paths/v1_signin_start.yaml @@ -21,3 +21,5 @@ post: application/json: schema: $ref: ../components/schemas/SignInStartResponse.yaml + security: + - app-id: [] diff --git a/docs/openapi/paths/v1_signup_finish.yaml b/docs/openapi/paths/v1_signup_finish.yaml index 9a6219e..dc25d6d 100644 --- a/docs/openapi/paths/v1_signup_finish.yaml +++ b/docs/openapi/paths/v1_signup_finish.yaml @@ -19,3 +19,5 @@ post: application/json: schema: $ref: ../components/schemas/SignUpFinishResponse.yaml + security: + - app-api-key: [] diff --git a/docs/openapi/paths/v1_signup_start.yaml b/docs/openapi/paths/v1_signup_start.yaml index 9976acf..2406e71 100644 --- a/docs/openapi/paths/v1_signup_start.yaml +++ b/docs/openapi/paths/v1_signup_start.yaml @@ -21,3 +21,5 @@ post: application/json: schema: $ref: ../components/schemas/SignUpStartResponse.yaml + security: + - app-id: [] \ No newline at end of file From eba44617f237465309cc9428772b246109220a6f Mon Sep 17 00:00:00 2001 From: Victor Stanchev Date: Sat, 29 Mar 2025 08:36:11 +0200 Subject: [PATCH 7/7] Update schema options type from object to string --- docs/openapi/components/schemas/SignInStartResponse.yaml | 6 +----- docs/openapi/components/schemas/SignUpStartResponse.yaml | 8 ++------ 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/docs/openapi/components/schemas/SignInStartResponse.yaml b/docs/openapi/components/schemas/SignInStartResponse.yaml index 888b398..748364d 100644 --- a/docs/openapi/components/schemas/SignInStartResponse.yaml +++ b/docs/openapi/components/schemas/SignInStartResponse.yaml @@ -8,11 +8,7 @@ properties: type: boolean description: Indicates whether or not this account exists options: - type: object + type: string description: > JSON object representing the options argument that should be passed to `navigator.credentials.get(options)` if `accountExists` is `true` or to `navigator.credentials.create(options)` if `accountExists` is `false`. - x-field-extra-annotation: '@com.fasterxml.jackson.annotation.JsonRawValue' - properties: - publicKey: - type: object diff --git a/docs/openapi/components/schemas/SignUpStartResponse.yaml b/docs/openapi/components/schemas/SignUpStartResponse.yaml index c6342b1..0906fda 100644 --- a/docs/openapi/components/schemas/SignUpStartResponse.yaml +++ b/docs/openapi/components/schemas/SignUpStartResponse.yaml @@ -7,9 +7,5 @@ properties: type: string description: Unique identifier for the sign-up request. options: - type: object - description: Options to pass to `navigator.credentials.create()` - x-field-extra-annotation: '@com.fasterxml.jackson.annotation.JsonRawValue' - properties: - publicKey: - type: object + type: string + description: Options to pass to `navigator.credentials.create()` serialized in a JSON string \ No newline at end of file