Search code examples
spring-securityspring-authorization-server

spring-security-oauth2-authorization-server + angular-auth-oidc-client


I am using

"angular-auth-oidc-client": "^13.1.0",
 <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-oauth2-authorization-server</artifactId>
            <version>0.2.2</version>
 </dependency>

I am using "OpenId Connect" in my App. My app works fine if i use a Keycloak. I like to replace it with spring-security-oauth2-authorization-server. I am facing some problems i can not solve.

I hit my app and then i am redirected to the login UI. I enter User + PW and give Consent. Then i send back to my app again. Here i see the following problem

enter image description here

enter image description here

There is no data in the "this.storagePersistenceService" that matches.

Do you have any Idee what could be wrong

Best regards G




import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.ClientSettings;
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.List;
import java.util.UUID;

/**
 * @author Joe Grandja
 * @since 0.0.1
 */
@Configuration(proxyBeanMethods = false)
public class AuthorizationServerConfigNew {

    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(List.of("*"));
        configuration.setAllowedMethods(List.of("*"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
        return http.formLogin(Customizer.withDefaults()).cors().configurationSource(corsConfigurationSource()).and().build();
    }

    // @formatter:off
    @Bean
    public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {
        RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
                .clientId("medical-share-openid-connect-client-id")
                .clientSecret("{noop}secret")
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
                .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
                .redirectUri("http://127.0.0.1:4200/")
                .redirectUri("http://127.0.0.1:4200")
                .redirectUri("http://localhost:4200/")
//                .redirectUri("http://127.0.0.1:4200/login/oauth2/code/messaging-client-oidc")
//                .redirectUri("http://127.0.0.1:4200/authorized")
                .scope(OidcScopes.OPENID)
                .scope("offline_access")
//                .scope("message.read")
//                .scope("message.write")
                .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
                .build();

        // Save registered client in db as if in-memory
        JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);
        registeredClientRepository.save(registeredClient);

        return registeredClientRepository;
    }
    // @formatter:on

    @Bean
    public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
        return new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);
    }

    @Bean
    public OAuth2AuthorizationConsentService authorizationConsentService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
        return new JdbcOAuth2AuthorizationConsentService(jdbcTemplate, registeredClientRepository);
    }

    @Bean
    public JWKSource<SecurityContext> jwkSource() {
        RSAKey rsaKey = Jwks.generateRsa();
        JWKSet jwkSet = new JWKSet(rsaKey);
        return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
    }

    @Bean
    public ProviderSettings providerSettings() {
//        return ProviderSettings.builder().issuer("http://auth-server:9000").build();
        return ProviderSettings.builder().issuer("http://localhost:8088").build();
    }

    @Bean
    public EmbeddedDatabase embeddedDatabase() {
        // @formatter:off
        return new EmbeddedDatabaseBuilder()
                .generateUniqueName(true)
                .setType(EmbeddedDatabaseType.H2)
                .setScriptEncoding("UTF-8")
                .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql")
                .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql")
                .addScript("org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql")
                .build();
        // @formatter:on
    }

}


import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

import static org.springframework.security.config.Customizer.withDefaults;
@EnableWebSecurity
public class DefaultSecurityConfigNew {

    // @formatter:off
    @Bean
    SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeRequests(authorizeRequests ->
                        authorizeRequests.anyRequest().authenticated()
                )
                .formLogin(withDefaults());
        return http.build();
    }
    // @formatter:on

    // @formatter:off
    @Bean
    UserDetailsService users() {
        UserDetails user = User.withDefaultPasswordEncoder()
                .username("U-294b70ca67df4c018f59950f35314944")
                .password("U-294b70ca67df4c018f59950f35314944")
                .roles("USER")
                .build();
        return new InMemoryUserDetailsManager(user);
    }
    // @formatter:on

}
/*
 * Copyright 2020-2021 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
 *
 *      https://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.
 */


import com.nimbusds.jose.jwk.Curve;
import com.nimbusds.jose.jwk.ECKey;
import com.nimbusds.jose.jwk.OctetSequenceKey;
import com.nimbusds.jose.jwk.RSAKey;

import javax.crypto.SecretKey;
import java.security.KeyPair;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.UUID;

/**
 * @author Joe Grandja
 * @since 0.1.0
 */
public final class Jwks {

    private Jwks() {
    }

    public static RSAKey generateRsa() {
        KeyPair keyPair = KeyGeneratorUtils.generateRsaKey();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        // @formatter:off
        return new RSAKey.Builder(publicKey)
                .privateKey(privateKey)
                .keyID(UUID.randomUUID().toString())
                .build();
        // @formatter:on
    }

    public static ECKey generateEc() {
        KeyPair keyPair = KeyGeneratorUtils.generateEcKey();
        ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();
        ECPrivateKey privateKey = (ECPrivateKey) keyPair.getPrivate();
        Curve curve = Curve.forECParameterSpec(publicKey.getParams());
        // @formatter:off
        return new ECKey.Builder(curve, publicKey)
                .privateKey(privateKey)
                .keyID(UUID.randomUUID().toString())
                .build();
        // @formatter:on
    }

    public static OctetSequenceKey generateSecret() {
        SecretKey secretKey = KeyGeneratorUtils.generateSecretKey();
        // @formatter:off
        return new OctetSequenceKey.Builder(secretKey)
                .keyID(UUID.randomUUID().toString())
                .build();
        // @formatter:on
    }
}

/*
 * Copyright 2020-2021 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
 *
 *      https://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.
 */


import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.spec.ECFieldFp;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.EllipticCurve;

/**
 * @author Joe Grandja
 * @since 0.1.0
 */
final class KeyGeneratorUtils {

    private KeyGeneratorUtils() {
    }

    static SecretKey generateSecretKey() {
        SecretKey hmacKey;
        try {
            hmacKey = KeyGenerator.getInstance("HmacSha256").generateKey();
        } catch (Exception ex) {
            throw new IllegalStateException(ex);
        }
        return hmacKey;
    }

    static KeyPair generateRsaKey() {
        KeyPair keyPair;
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            keyPairGenerator.initialize(2048);
            keyPair = keyPairGenerator.generateKeyPair();
        } catch (Exception ex) {
            throw new IllegalStateException(ex);
        }
        return keyPair;
    }

    static KeyPair generateEcKey() {
        EllipticCurve ellipticCurve = new EllipticCurve(
                new ECFieldFp(
                        new BigInteger("115792089210356248762697446949407573530086143415290314195533631308867097853951")),
                new BigInteger("115792089210356248762697446949407573530086143415290314195533631308867097853948"),
                new BigInteger("41058363725152142129326129780047268409114441015993725554835256314039467401291"));
        ECPoint ecPoint = new ECPoint(
                new BigInteger("48439561293906451759052585252797914202762949526041747995844080717082404635286"),
                new BigInteger("36134250956749795798585127919587881956611106672985015071877198253568414405109"));
        ECParameterSpec ecParameterSpec = new ECParameterSpec(
                ellipticCurve,
                ecPoint,
                new BigInteger("115792089210356248762697446949407573529996955224135760342422259061068512044369"),
                1);

        KeyPair keyPair;
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
            keyPairGenerator.initialize(ecParameterSpec);
            keyPair = keyPairGenerator.generateKeyPair();
        } catch (Exception ex) {
            throw new IllegalStateException(ex);
        }
        return keyPair;
    }
}

import {NgModule} from '@angular/core';
import {AuthModule, LogLevel} from 'angular-auth-oidc-client';

@NgModule({
  imports: [
    AuthModule.forRoot({
      config: {
        configId: 'ms-angular-auth-oidc-client-lib-id',
        logLevel: LogLevel.Debug,
        historyCleanupOff: false,
        authority: 'http://localhost:8088',
        redirectUrl: 'http://127.0.0.1:4200' + '/',
        postLogoutRedirectUri: 'http://127.0.0.1:4200' + '/',
        clientId: 'medical-share-openid-connect-client-id',
        scope: 'openid offline_access',
        responseType: 'code',
        silentRenew: true,
        renewTimeBeforeTokenExpiresInSeconds: 30,
        ignoreNonceAfterRefresh: true,
        useRefreshToken: true,
        autoUserInfo: false, // change that as it is in the example for spring security like this
        secureRoutes: [
          'http://localhost:4200/',
          'localhost:4200/',
          '127.0.0.1:4200/',
          'http://127.0.0.1:4200/',
          'http://localhost:4200/',
          'http://localhost:8081',
          'localhost:8081',
        ],
      },
    }),
  ],
  exports: [AuthModule],
})
export class AuthConfigModule {}

09:38:13.475 Navigated to http://localhost:4200/should-login
09:38:13.682 [webpack-dev-server] Live Reloading enabled. index.js:548
09:38:13.875 [DEBUG] ms-angular-auth-oidc-client-lib-id - Did not find any configured route for route http://localhost:8088/.well-known/openid-configuration angular-auth-oidc-client.mjs:160:20
09:38:13.878 [DEBUG] ms-angular-auth-oidc-client-lib-id - Did not find any configured route for route ../../assets/config/config.json angular-auth-oidc-client.mjs:160:20
09:38:13.956 Angular is running in development mode. Call enableProdMode() to enable production mode. core.mjs:24856:16
09:38:13.967 [DEBUG] ms-angular-auth-oidc-client-lib-id - Working with config 'ms-angular-auth-oidc-client-lib-id' using http://localhost:8088 angular-auth-oidc-client.mjs:160:20
09:38:13.968 [DEBUG] ms-angular-auth-oidc-client-lib-id - currentUrl to check auth with:  http://localhost:4200/should-login angular-auth-oidc-client.mjs:157:20
09:38:13.969 [DEBUG] ms-angular-auth-oidc-client-lib-id - checkAuth completed - firing events now. isAuthenticated: false angular-auth-oidc-client.mjs:160:20
09:38:13.970 Navigated to http://localhost:8088/oauth2/authorize?client_id=medical-share-openid-connect-client-id&redirect_uri=http%3A%2F%2F127.0.0.1%3A4200%2F&response_type=code&scope=openid%20offline_access&nonce=e9bd6b927b16552d4560ef708d5fd8ca49mFmzaxQ&state=771502df2eae52657faf84e1e9cf3253b3TN7VCvc&code_challenge=0AggJb7Z2ONJmNU7rAqXh9BmKf5YeEcrLlmw9lgoMJE&code_challenge_method=S256
09:38:13.970 [DEBUG] ms-angular-auth-oidc-client-lib-id - BEGIN Authorize OIDC Flow, no auth data angular-auth-oidc-client.mjs:160:20
09:38:13.971 [DEBUG] ms-angular-auth-oidc-client-lib-id - Nonce created. nonce:e9bd6b927b16552d4560ef708d5fd8ca49mFmzaxQ angular-auth-oidc-client.mjs:160:20
09:38:13.971 [DEBUG] ms-angular-auth-oidc-client-lib-id - Authorize created. adding myautostate: 771502df2eae52657faf84e1e9cf3253b3TN7VCvc angular-auth-oidc-client.mjs:160:20
09:38:13.994 downloadable font: download failed (font-family: "sdx-icons" style:normal weight:400 stretch:100 src index:1): status=2152398850 source: http://localhost:4200/sdx-icons-p.woff2
09:38:14.019 [webpack-dev-server] Disconnected! index.js:548
09:38:14.021 [webpack-dev-server] Trying to reconnect... index.js:548
09:38:16.459 Navigated to http://localhost:8088/login
09:38:16.801 [webpack-dev-server] Live Reloading enabled. index.js:548
09:38:16.983 [DEBUG] ms-angular-auth-oidc-client-lib-id - Did not find any configured route for route http://localhost:8088/.well-known/openid-configuration angular-auth-oidc-client.mjs:160:20
09:38:16.988 [DEBUG] ms-angular-auth-oidc-client-lib-id - Did not find any configured route for route ../../assets/config/config.json angular-auth-oidc-client.mjs:160:20
09:38:17.056 Angular is running in development mode. Call enableProdMode() to enable production mode. core.mjs:24856:16
09:38:17.069 ERROR Error: Uncaught (in promise): () => new Error(`could not find matching config for state ${stateParamFromUrl}`)
    Angular 19
    RxJS 21
    ZoneAwarePromise Angular
    toPromise RxJS
    getEnvironmentConfiguration config.service.ts:25
    initAppConfig init.app.config.ts:6
    Angular 24
    7004 main.ts:14
    Webpack 7
core.mjs:6484:12

enter image description here






21.3.22 I was able to make the spring-security-oauth2-authorization-server work with the config below.

  • The autoUserInfo does not work, so i switched it off.
  • See defaultSecurityFilterChain, i had to add a .permitAll() otherwise the endpoint for the auth flow where protected. That feels somehow wrong.
  • RegisteredClient: took the config from the example @SteveRiesenberg mendtiond

@SteveRiesenberg do you see any improvments / simplification possible in that code?



import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.ClientSettings;
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.UUID;

/**
 * @author Joe Grandja
 * @since 0.0.1
 */
@Configuration(proxyBeanMethods = false)
public class AuthorizationServerConfigNew {

    public static CorsConfigurationSource corsConfigurationSource() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        config.addAllowedOrigin("*");
//        config.setAllowCredentials(true);
        source.registerCorsConfiguration("/**", config);
        return source;
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
        return http.formLogin(Customizer.withDefaults()).cors().configurationSource(corsConfigurationSource()).and().build();
    }

    // @formatter:off
    @Bean
    public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {
        RegisteredClient publicClient = RegisteredClient.withId(UUID.randomUUID().toString())
                .clientId("medical-share-openid-connect-client-id")
                .clientAuthenticationMethod(ClientAuthenticationMethod.NONE)
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .redirectUri("http://127.0.0.1:4200")
                .redirectUri("http://127.0.0.1:4200/")
                .redirectUri("http://127.0.0.1:4200/silent-renew.html")
                .scope(OidcScopes.OPENID)
                .scope("offline_access")
//                .scope("message.read")
//                .scope("message.write")
                .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).requireProofKey(true).build())
                .build();

        // Save registered client in db as if in-memory
        JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);
        registeredClientRepository.save(publicClient);

        return registeredClientRepository;
    }
    // @formatter:on

    @Bean
    public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
        return new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);
    }

    @Bean
    public OAuth2AuthorizationConsentService authorizationConsentService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
        return new JdbcOAuth2AuthorizationConsentService(jdbcTemplate, registeredClientRepository);
    }

    @Bean
    public JWKSource<SecurityContext> jwkSource() {
        RSAKey rsaKey = Jwks.generateRsa();
        JWKSet jwkSet = new JWKSet(rsaKey);
        return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
    }

    @Bean
    public ProviderSettings providerSettings() {
        return ProviderSettings.builder().issuer("http://127.0.0.1:8088").build();
//        return ProviderSettings.builder().issuer("http://localhost:8088").build();
    }

    @Bean
    public EmbeddedDatabase embeddedDatabase() {
        // @formatter:off
        return new EmbeddedDatabaseBuilder()
                .generateUniqueName(true)
                .setType(EmbeddedDatabaseType.H2)
                .setScriptEncoding("UTF-8")
                .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql")
                .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql")
                .addScript("org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql")
                .build();
        // @formatter:on
    }

}


import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

import static com.test.kubernetes.authorizationserver.application.sample.config.AuthorizationServerConfigNew.corsConfigurationSource;
import static org.springframework.security.config.Customizer.withDefaults;

@EnableWebSecurity
public class DefaultSecurityConfigNew {

    // @formatter:off
    @Bean
    SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        http.cors().configurationSource(corsConfigurationSource()).and()
                .authorizeRequests()
//                .antMatchers("/.well-known/**", "/oauth2/**", "/userinfo")
                .antMatchers("/**")
                .permitAll()
                .and()
                .authorizeRequests()
                .antMatchers("/login")
                .authenticated()
                .and()
                .formLogin(withDefaults());

        return http.cors().configurationSource(corsConfigurationSource()).and().build();
    }
    // @formatter:on

    // @formatter:off
    @Bean
    UserDetailsService users() {
        UserDetails user = User.withDefaultPasswordEncoder()
                .username("U-294b70ca67df4c018f59950f35314944")
                .password("U-294b70ca67df4c018f59950f35314944")
                .roles("USER")
                .build();
        return new InMemoryUserDetailsManager(user);
    }
    // @formatter:on

}
import {NgModule} from '@angular/core';
import {AuthModule, LogLevel} from 'angular-auth-oidc-client';

@NgModule({
  imports: [
    AuthModule.forRoot({
      config: {
        configId: 'ms-angular-auth-oidc-client-lib-id',
        logLevel: LogLevel.Debug,
        historyCleanupOff: false,
        // authority: 'http://localhost:8088/realms/master',
        authority: 'http://127.0.0.1:8088',
        redirectUrl: 'http://127.0.0.1:4201/',
        // redirectUrl: 'http://localhost:4201/',
        postLogoutRedirectUri: 'http://127.0.0.1:4201/',
        clientId: 'medical-share-openid-connect-client-id',
        scope: 'openid offline_access',
        responseType: 'code',
        silentRenew: true,
        renewTimeBeforeTokenExpiresInSeconds: 10,
        autoUserInfo: false,
        ignoreNonceAfterRefresh: true,
        useRefreshToken: true,
        // autoCleanStateAfterAuthentication: false,
        secureRoutes: [
          'http://localhost:4201/',
          'localhost:4201/',
          '127.0.0.1:4201/',
          'http://127.0.0.1:4201/',
          'http://127.0.0.1:4201',
          'http://localhost:4201/',
          'http://localhost:8083',
          'localhost:8083',
          'http://127.0.0.1:8088',
          'http://127.0.0.1:8088/',
        ],
      },
    }),
  ],
  exports: [AuthModule],
})
export class AuthConfigModule {
}

Solution

  • Finally i made it :-) Thanks a lot to @Steve.

    Note that i am aware that there is no RefreshToken support. Therefore i made the lifetime of a token 1 day.

    
    import com.nimbusds.jose.JOSEException;
    import com.nimbusds.jose.jwk.RSAKey;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.annotation.PostConstruct;
    import java.lang.invoke.MethodHandles;
    import java.security.KeyPair;
    import java.security.KeyPairGenerator;
    import java.security.interfaces.RSAPrivateKey;
    import java.security.interfaces.RSAPublicKey;
    import java.util.UUID;
    
    @RestController
    @RequestMapping()
    public class OpenIdConnectMockController {
    
        static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
    
        private KeyPair rsaKeyPair;
        private RSAKey jwkRsaPublicKey;
    
        @PostConstruct
        public void generateKey() {
            this.rsaKeyPair = generateRsaKey();
            this.jwkRsaPublicKey = generateRsa(this.rsaKeyPair);
        }
    
        /**
         * Note this method is currently not use, but here for future use.
         * We use the same URL as in the Keycloak, so we have not to change things when running the thing against ms-backend-test-openid-connect-mock or Keycloak.
         */
        @GetMapping(path = "/realms/master/protocol/openid-connect/certs", produces = "application/json")
        public String keys() {
            logger.info("Keys was called {}", this.jwkRsaPublicKey.toString());
            return "{\"keys\":[" + this.jwkRsaPublicKey.toString() + "]}";
        }
    
        /**
         * This is used in the SystemTest. There we download the private key and sign the JWT.
         */
        @GetMapping(path = "/realms/master/protocol/openid-connect/private-key", produces = "application/json")
        public byte[] getPrivateKey() throws JOSEException {
            RSAKey privateKey = new RSAKey.Builder((RSAPublicKey) this.rsaKeyPair.getPublic()).privateKey(this.rsaKeyPair.getPrivate()).build();
            return privateKey.toRSAPrivateKey().getEncoded();
        }
    
        public RSAKey getJwkRsaPublicKey() {
            return this.jwkRsaPublicKey;
        }
    
        private RSAKey generateRsa(KeyPair keyPair) {
            RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
            RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
            return new RSAKey.Builder(publicKey)
                    .privateKey(privateKey)
                    .keyID(UUID.randomUUID().toString())
                    .build();
        }
    
        private KeyPair generateRsaKey() {
            KeyPair keyPair;
            try {
                KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
                keyPairGenerator.initialize(2048);
                keyPair = keyPairGenerator.generateKeyPair();
            } catch (Exception ex) {
                throw new IllegalStateException(ex);
            }
            return keyPair;
        }
    
    }
    
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.core.userdetails.User;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.provisioning.InMemoryUserDetailsManager;
    
    @Configuration
    public class PersistedUserConfig {
    
        @Bean
        UserDetailsService users() {
            UserDetails user = User.withDefaultPasswordEncoder()
                    .username("U-294b70ca67df4c018f59950f35314944")
                    .password("U-294b70ca67df4c018f59950f35314944")
                    .roles("USER")
                    .build();
            return new InMemoryUserDetailsManager(user);
        }
    
    }
    
    
    
    import com.nimbusds.jose.jwk.JWKSet;
    import com.nimbusds.jose.jwk.RSAKey;
    import com.nimbusds.jose.jwk.source.JWKSource;
    import com.nimbusds.jose.proc.SecurityContext;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.Ordered;
    import org.springframework.core.annotation.Order;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
    import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
    import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.userdetails.User;
    import org.springframework.security.oauth2.core.AuthorizationGrantType;
    import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
    import org.springframework.security.oauth2.core.oidc.OidcScopes;
    import org.springframework.security.oauth2.server.authorization.*;
    import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
    import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
    import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
    import org.springframework.security.oauth2.server.authorization.config.ClientSettings;
    import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
    import org.springframework.security.oauth2.server.authorization.config.TokenSettings;
    import org.springframework.security.web.SecurityFilterChain;
    import org.springframework.web.cors.CorsConfiguration;
    import org.springframework.web.cors.CorsConfigurationSource;
    import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
    
    import javax.annotation.PostConstruct;
    import java.lang.invoke.MethodHandles;
    import java.time.Duration;
    import java.util.Objects;
    import java.util.UUID;
    
    import static org.springframework.security.config.Customizer.withDefaults;
    
    @EnableWebSecurity
    @Configuration(proxyBeanMethods = false)
    public class TestAuthorizationServerConfig {
    
        private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
    
    
        @PostConstruct
        public void publicNote() {
            logger.info("****************************************************************************************************************************");
            logger.info("****************************************************************************************************************************");
            logger.info("Note that the current spring authorisation server does not support token renewal. If you want to test that, use the Keycloak");
            logger.info("****************************************************************************************************************************");
            logger.info("****************************************************************************************************************************");
        }
    
        public static CorsConfigurationSource corsConfigurationSource() {
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
            CorsConfiguration config = new CorsConfiguration();
            config.addAllowedHeader("*");
            config.addAllowedMethod("*");
    
            config.addAllowedOrigin("127.0.0.1:4201");
            config.addAllowedOrigin("http://127.0.0.1:4201");
            config.addAllowedOrigin("localhost:4201");
    
            config.addAllowedOrigin("127.0.0.1:4200");
            config.addAllowedOrigin("http://127.0.0.1:4200");
            config.addAllowedOrigin("localhost:4200");
    
            config.setAllowCredentials(true);
            source.registerCorsConfiguration("/**", config);
            return source;
        }
    
        @Bean
        SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
            return http.cors().configurationSource(corsConfigurationSource()).and()
                    .authorizeRequests()
                    .antMatchers("/**")
                    .permitAll()
                    .and()
                    .authorizeRequests()
                    .antMatchers("/login")
                    .authenticated()
                    .and()
                    .formLogin(withDefaults())
                    .build();
        }
    
        @Bean
        @Order(Ordered.HIGHEST_PRECEDENCE)
        public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
            OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
            return http.formLogin(withDefaults()).cors().configurationSource(corsConfigurationSource()).and().build();
        }
    
        @Bean
        public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {
            RegisteredClient publicClient = RegisteredClient.withId(UUID.randomUUID().toString())
                    .clientId("medical-share-openid-connect-client-id")
                    .clientAuthenticationMethod(ClientAuthenticationMethod.NONE)
                    .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
    
                    .redirectUri("http://127.0.0.1:4200")
                    .redirectUri("http://127.0.0.1:4200/")
    
                    .redirectUri("http://127.0.0.1:4201")
                    .redirectUri("http://127.0.0.1:4201/")
                    .scope(OidcScopes.OPENID)
                    .tokenSettings(TokenSettings.builder().accessTokenTimeToLive(Duration.ofDays(1)).build())
                    .scope("offline_access")
    //                .scope("message.read")
    //                .scope("message.write")
                    .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).requireProofKey(true).build())
                    .build();
    
            // Save registered client in db as if in-memory
            JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);
            registeredClientRepository.save(publicClient);
    
            return registeredClientRepository;
        }
    
        @Bean
        public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
            return new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);
        }
    
        @Bean
        public OAuth2AuthorizationConsentService authorizationConsentService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
            return new JdbcOAuth2AuthorizationConsentService(jdbcTemplate, registeredClientRepository);
        }
    
        @Bean
        public JWKSource<SecurityContext> jwkSource(OpenIdConnectMockController openIdConnectMockController) {
            RSAKey rsaKey = openIdConnectMockController.getJwkRsaPublicKey();
            JWKSet jwkSet = new JWKSet(rsaKey);
            return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
        }
    
        @Bean
        public ProviderSettings providerSettings() {
            return ProviderSettings.builder().issuer("http://127.0.0.1:8088").build();
        }
    
        @Bean
        public EmbeddedDatabase embeddedDatabase() {
            return new EmbeddedDatabaseBuilder()
                    .generateUniqueName(true)
                    .setType(EmbeddedDatabaseType.H2)
                    .setScriptEncoding("UTF-8")
                    .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql")
                    .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql")
                    .addScript("org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql")
                    .build();
        }
    
        /**
         * This is a bit a hack, but as we do not know how we integrate the HealthPlattform this is a very easy way to solve the Problem for the moment.
         */
        @Bean
        public OAuth2TokenCustomizer<JwtEncodingContext> tokenCustomizer() {
            return context -> {
                Authentication principal = context.getPrincipal();
                if (Objects.equals(context.getTokenType().getValue(), "access_token") && principal instanceof UsernamePasswordAuthenticationToken) {
                    User user = (User) principal.getPrincipal();
                    context.getClaims()
                            .claim("name", user.getUsername());
                }
            };
        }
    
    }
    
    
    import {NgModule} from '@angular/core';
    import {AuthModule, LogLevel} from 'angular-auth-oidc-client';
    
    @NgModule({
      imports: [
        AuthModule.forRoot({
          config: {
            configId: 'ms-angular-auth-oidc-client-lib-id',
            logLevel: LogLevel.Debug,
            historyCleanupOff: false,
            // Keycloak
            // authority: 'http://localhost:8088/realms/master',
            authority: 'http://127.0.0.1:8088',
            redirectUrl: 'http://127.0.0.1:4201/',
            // Keycloak
            // redirectUrl: 'http://localhost:4201/',
            postLogoutRedirectUri: 'http://127.0.0.1:4201/',
            clientId: 'medical-share-openid-connect-client-id',
            scope: 'openid',
            responseType: 'code',
            silentRenew: true,
            renewTimeBeforeTokenExpiresInSeconds: 10,
            autoUserInfo: false,
            useRefreshToken: true,
            secureRoutes: [
              'http://127.0.0.1:4201',
              'http://localhost:4201',
            ],
          },
        }),
      ],
      exports: [AuthModule],
    })
    export class AuthConfigModule {
    }