Unable to get access_token, Refresh token using client_assertion in Spring oauth2 Server - spring-boot

generated client assertion like below using PKCS12, still I get invalid client error. Need your help to resolve this issue. Looks like authorization server is not compatible with certificate generated RSAkey. I am badly stuck here. base branch used to create project is https://github.com/jgrandja/spring-authorization-server/tree/jwt-client-authn/samples/default-authorizationserver
InputStream fm = new FileInputStream(pathPKCS12);
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(fm, pwdPKCS12.toCharArray());
Key key = keystore.getKey(keystore.aliases().nextElement(), pwdPKCS12.toCharArray());
Certificate cert = keystore.getCertificate(keystore.aliases().nextElement());
PublicKey publicKey = cert.getPublicKey();
KeyPair keyPair = new KeyPair(publicKey, (PrivateKey) key);
RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
Instant now = Instant.now();
//The JWT signature algorithm we will be using to sign the token
//SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.RS256;
String jwt= Jwts.builder()
.setAudience("https://localhost:9000")
.setIssuedAt(Date.from(now))
.setExpiration(Date.from(now.plus(5L, ChronoUnit.DAYS)))
.setIssuer("profinch")
.setSubject("profinch")
.setId(UUID.randomUUID().toString())
.signWith(rsaPrivateKey)
.compact();
System.out.println(jwt);
error is below -
postman printscreen where error is displayed
post man request x-www-form-URL encoded is -
client_assertion_type:urn:ietf:params:oauth:client-assertion-type:jwt-bearer
client_assertion:eyJraWQiOiJKWHYwMXhuNW83eDFuTXB1SVV1Q1gyNDdPbnhMMGdMeGV2VUFnLW0xbjdvIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJhdWQiOiJodHRwczovL2xvY2FsaG9zdDo5MDAwL29hdXRoMi90b2tlbiIsImlhdCI6MTY3NTY3NDU4NywiZXhwIjoxNjc2MTA2NTg3LCJpc3MiOiJwcm9maW5jaCIsInN1YiI6InByb2ZpbmNoIiwianRpIjoiYTFiYzdiZTktZGY3Zi00NjhkLTkwMzktNTZjOTE4NmMwNDkwIn0.LjyF122kDSTlkUzVomOpnqwpHxNEdeq73fVx4Zzcu1dEmChWpk2LB1NbDhOPTIDNpGPF4aB8RkHkZfTmbA9TFYrm20MdHYFt5fRliBILUdBuNjRok3EEmNmWnjFTyfwoufMGT-sX6orLWkNUmsOkaywGIKCgHgR-MfEpTZ_v0cNL3uSuWnDQAy6yZgm9kWt_jNZdPGouBFHGsoZxhr2XAFrwhlQVq0VDOTELX7ylDcqOR5phHxAfubx_mW79ns1Que3jO-o_mvYtXTamoOq_DiJD-xpRnAQEq36czoSHvPYVrjlLhCloBtuedzD0gODmypVc_0Dq2dPp4-y9pCiV-g
Auth-server client registration as below-
RegisteredClient registeredClient2 = RegisteredClient.withId(UUID.randomUUID().toString()) .clientId("profinch") .clientAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) .redirectUri(clientModel.getRedirectUri()) .scope(OidcScopes.OPENID).scope(OidcScopes.PROFILE).scope("message.read").scope("message.write") .clientSettings(ClientSettings.builder() .requireAuthorizationConsent(false) .jwkSetUrl("http://localhost:8080/jwks") .tokenEndpointAuthenticationSigningAlgorithm(SignatureAlgorithm.RS256) .build()) .build();
Should be able to get acces_token, refresh_oken using client_assertion in postman as per printscreen shared.

Related

NextJs middleware can't verify JWT from java spring backend

Im using Nextjs mainly for my frontend and Java springboot for my backend. However I want to use the nextjs middleware for protecting my routes via JWT. Since jsonwebtoken can't run on edge functions I am using jose.
After attempting to grab the JWT from cookies, I try to verify it and get this message:
JWSSignatureVerificationFailed: signature verification failed
Here is the signing of my token in the spring backend:
public static String generateJwtToken(AppUser user) {
Map<String, Object> claims = new HashMap<>();
return Jwts.builder()
.setClaims(claims)
.setSubject(user.getUsername())
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + TOKEN_VALIDITY * 1000))
.signWith(SignatureAlgorithm.HS512, jwtSecret)
.compact();
}
and here is the attempt to verify it in my nextjs middleware file:
const token = request.cookies.get('jwt');
const { payload } = await jwtVerify(
token,
new TextEncoder().encode(process.env.SECRET_KEY)
);
Note: the jwtSecret and SECRET_KEY are the exact same values coming from the exact same .env file. Any help would be greatly appreciated
Found my answer more or less here https://github.com/auth0/node-jsonwebtoken/issues/208#issuecomment-231861138. This exact comment explains what should be happening with the secret key.
As for what I fixed in the code, I had to return the secret key as a bytes array.
.signWith(SignatureAlgorithm.HS512, jwtSecret.getBytes("UTF-8"))

How to configure sslContext with p12 store (and password) for client authentication with https endpoint in Spring/Kotlin

I want to call a https protected endpoint using a certificate and private key. I received a .p12 keystore which is protected with a password.
For testing purposes I extracted the .cer file and private key using openssl.
I could verify locally that communication works by setting the ssl context like this:
fun test(): WebClient.RequestHeadersSpec<*> {
val sslContext = SslContextBuilder
.forClient()
.keyManager(ClassPathResource("cert").inputStream, ClassPathResource("key").inputStream)
.build()
val httpClient: HttpClient = HttpClient.create().secure { sslSpec -> sslSpec.sslContext(sslContext) }
val webClient = WebClient
.builder()
.baseUrl("baseUrl")
.clientConnector(ReactorClientHttpConnector(httpClient))
.build()
return webClient
.post()
.uri("uri")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue("test")
}
However, I do not want to version the cert and private key in my repository. How would I set the sslContext with the .p12 keystore and the password? I did not find any examples for this scenario.
load file from disc
this is java but it should be easy to adapt to kotlin
KeyStore keyStore = KeyStore.getInstance("pkcs12");
keyStore.load(new FileInputStream(keyStoreLocation), keyStorePassword.toCharArray());
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
new SSLContextBuilder()
.loadKeyMaterial(keyStore, keyStorePassword.toCharArray())
.build(),
NoopHostnameVerifier.INSTANCE);
HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory).build();
where file location and password can provided from configuration
#Value("${keyStore.location}")
private String keyStoreLocation;
#Value("${keyStore.password}")
private String keyStorePassword;

Changing a Spring oauth2ResourceServer JWT ex. to use a single secret instead of a Keypair throws an exception “Failed to select a JWK signing key”

I'm trying to implement a Spring Boot Rest backend with JWT-security, based on Springs new authorization server and this example: https://github.com/spring-projects/spring-security-samples/tree/main/servlet/spring-boot/java/jwt/login
It uses Asymmetric keys to sign and verify tokens which seems like an overkill, since both authentication (where the token is generated) and authorization (verified) happens on the same server. So, to simplify deployment (just pass in a single secret via an environment variable), I have been trying to rewrite it to use a single shared secret.
The example code implements two Bean-components, one to create the JwtEncoder (using a private RSA key) and one for the JWTDecoder (using the matching public key).
I have rewritten the Decoder as explained in chapter 15 in the book “Spring Security in Action” so I assume this should work, since the NimbusJwtDecoder offers a withSecretKey method.
//Will eventually come via an environment variable
static byte[] secret = "j8IoV1jF67".getBytes();
#Bean
JwtDecoder jwtDecoder() {
// return NimbusJwtDecoder.withPublicKey(this.key).build();
SecretKey theKey = new SecretKeySpec(secret, 0, secret.length, "AES");
return NimbusJwtDecoder.withSecretKey(theKey).build();
}
I have implemented the Encoder, which is coursing the problem, like so (code commented out, is the original code using the private RSA Key:
#Bean
JwtEncoder jwtEncoder() {
// JWK jwk = new RSAKey.Builder(this.key).privateKey(this.priv).build();
// JWKSource<SecurityContext> jwks = new ImmutableJWKSet<>(new JWKSet(jwk));
// return new NimbusJwtEncoder(jwks);
SecretKey originalKey = new SecretKeySpec(secret, 0, secret.length, "AES");
JWKSource<SecurityContext> immutableSecret = new ImmutableSecret<SecurityContext>(originalKey);
return new NimbusJwtEncoder(immutableSecret);
}
When I login (via the POST /token endpoint) the line that uses the encoder:
return this.encoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();
Throws this exception
org.springframework.security.oauth2.jwt.JwtEncodingException: An error occurred while attempting to encode the Jwt: Failed to select a JWK signing key
at org.springframework.security.oauth2.jwt.NimbusJwtEncoder.selectJwk(NimbusJwtEncoder.java:134)
at org.springframework.security.oauth2.jwt.NimbusJwtEncoder.encode(NimbusJwtEncoder.java:108)
Any suggestions to how to implement this example with a simple shared secret, instead of asymmetric keys?

NTLM authentication in Apache HttpClient

I am building an application that reads JSON response from certain endpoints and I am trying to authenticate in Apache HttpClient using NTLM authentication:
The class that is responsible for authentication HttpConnector tries to authentice right after its instantiation:
public static HttpConnector on(String username, String password) {
HttpConnector connector = new HttpConnector(username, password);
Credentials credentials = new NTCredentials(username, password, "host", "domain");
connector.getHttpClient().getState().setCredentials(AuthScope.ANY, credentials);
return connector;
}
but I always get response code 401 Unauthorized. As I read in internet including here in Stackoverflow I used NTCredentials that I am trying to set globally in the HttpClient. I have tested the endpoints in Postman, there I get the JSON response successfully but HttpClient cannot connect.
In the code I use GetMethod: httpMethod = new GetMethod(url);
Here I have also tried to configure authentication but it still does not work:
private void configureMethod(HttpMethod httpMethod) throws MalformedChallengeException {
httpMethod.getHostAuthState().setAuthRequested(true);
httpMethod.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
httpMethod.setDoAuthentication(true);
httpMethod.getParams().setParameter(
HttpMethodParams.RETRY_HANDLER,
new DefaultHttpMethodRetryHandler(3, false));
httpMethod.getHostAuthState().setAuthRequested(true);
httpMethod.getHostAuthState().setAuthScheme(
new NTLMScheme(
String.format("ntlm %s:%s", username, password)));
}
During debugging I see that I get: Connection reset by peer: socket write error. It happens in HttpMethodDirector::executeWithRetry(final HttpMethod method) method.
Can someone help me, what is the correctNTLM authentication setup in Apache HttpClient. Can I really use the global set of credentials or I have to setup credentials to every HttpMethod I create and how?
Thank you in advance!
I fixed this by formatting the client the following way:
HttpClient httpClient = new DefaultHttpClient();
httpClient.getCredentialsProvider().setCredentials(
new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT),
new NTCredentials(username, password, HOST, MY_DOMAIN));
And the used not GetMethod but HttpGet:
HttpGet = getRequest = new HttpGet(url);
and this time the connection owas successful.

Spring Security 5.2 Password Flow

I am trying to authenticate the user using the password flow in the latest version of Spring Security - 5.2.
The docs seem to suggest how to do that.
#Bean
public OAuth2AuthorizedClientManager passwordFlowAuthorizedClientManager(
HttpClient httpClient,
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
DefaultPasswordTokenResponseClient c = new DefaultPasswordTokenResponseClient();
RestTemplate client = new RestTemplate(requestFactory);
client.setMessageConverters(Arrays.asList(
new FormHttpMessageConverter(),
new OAuth2AccessTokenResponseHttpMessageConverter()));
client.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
c.setRestOperations(client);
OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
.password(configurer -> configurer.accessTokenResponseClient(c))
.refreshToken()
.build();
DefaultOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
authorizedClientManager.setContextAttributesMapper(authorizeRequest -> {
Map<String, Object> contextAttributes = new HashMap<>();
String username = authorizeRequest.getAttribute(OAuth2ParameterNames.USERNAME);
String password = authorizeRequest.getAttribute(OAuth2ParameterNames.PASSWORD);
contextAttributes.put(OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME, username);
contextAttributes.put(OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME, password);
return contextAttributes;
});
return authorizedClientManager;
}
I execute the request, I can see the access token returned in HTTP header but the SecurityContext is not populated and the session user remains anonymous.
String username = "joe";
String password = "joe";
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
ClientRegistration r = clientRegistrationRepository.findByRegistrationId("keycloak");
OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(r.getRegistrationId())
.principal(authentication)
.attributes(attrs -> {
attrs.put(OAuth2ParameterNames.USERNAME, username);
attrs.put(OAuth2ParameterNames.PASSWORD, password);
})
.build();
OAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);
Any ideas?
After reading into the documentation a bit more I do not think that Oauth 2 password flow in Spring Security 5.2 is supported the same way authorisation flow is. Spring Security 5.2 has password flow support for the http client which can cache the authorization request and refresh the token before it expires - but there is no end user password flow support in which the client proxies the credentials to the authorization server.
Of course, it is entirely possible to authenticate the end user by harvesting the credentials, implementing a custom AuthenticationProvider that swaps the credentials for a token with the authorization server and returns an OAuth2AuthenticationToken that is persisted to the context.

Resources