Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support JWT for Client Authentication #8175

Closed
jgrandja opened this issue Mar 23, 2020 · 31 comments
Closed

Support JWT for Client Authentication #8175

jgrandja opened this issue Mar 23, 2020 · 31 comments
Assignees
Labels
in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose) type: enhancement A general enhancement
Milestone

Comments

@jgrandja
Copy link
Contributor

This feature will partially implement JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants.

Section 2.2. Using JWTs for Client Authentication will be the focus for this feature implementation.

NOTE: This ticket addresses client-side support only.

Related #6881 #6053

@jgrandja jgrandja self-assigned this Mar 23, 2020
@jgrandja jgrandja added in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose) type: enhancement A general enhancement labels Mar 23, 2020
@jgrandja jgrandja added this to the 5.4.x milestone Mar 23, 2020
@ghost
Copy link

ghost commented Apr 15, 2020

Cool. Authenticating with Azure AD using a certificate uses this flow, afaics. This feature will help us executing that flow cleanly. Thx.

@thomas-corte
Copy link

I believe that this would also provide better support for "Sign In With Apple", as Apple uses a non-static client secret, which in their case must be the string representation of a JWT (seemingly compliant with the JWT profile covered in the issue) with an expiration date of at most 6 months in the future (see https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens).

@jgrandja
Copy link
Contributor Author

jgrandja commented Feb 23, 2021

@thomas-corte Thanks for pointing that out. Indeed, Apple does support private_key_jwt client authentication so this feature will be needed.

FYI, we are targeting this for 5.5 but we're dependent on merging #9208 first, which we are getting close.

@thomas-corte
Copy link

thomas-corte commented Feb 24, 2021

Not sure whether this helps (as #9208 seems to aim at using the Nimbus libraries), but for comparison, I was able to successfully create a working secret for Apple using this code (which utilizes BouncyCastle and the io.jsonwebtoken library):

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import java.io.Reader;
import java.io.StringReader;

import java.security.Key;
import java.security.PrivateKey;

import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;

import org.bouncycastle.openssl.PEMParser;

import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;

...

JwtBuilder jwtBuilder = Jwts.builder ();

String keyBase64 = "-----BEGIN PRIVATE KEY-----\n" +
  "<base64 encoded private key data from Apple>" +
  "-----END PRIVATE KEY-----";

final Reader pemReader = new StringReader (keyBase64);
final PEMParser pemParser = new PEMParser (pemReader);
final JcaPEMKeyConverter converter = new JcaPEMKeyConverter ();

final PrivateKeyInfo object = (PrivateKeyInfo) pemParser.readObject ();
final PrivateKey pKey = converter.getPrivateKey (object);

Map<String, Object> headers = new HashMap<> ();
headers.put ("kid", "WTXXXXXX"); // Key ID from Apple
headers.put ("alg", "ES256");
jwtBuilder.setHeader (headers);

String clientSecret = jwtBuilder.
  setIssuer ("KVXXXXXXXX"). // Team ID from Apple
  setIssuedAt (new Date (System.currentTimeMillis ())).
  setExpiration (new Date (System.currentTimeMillis () + 180l * 86_400_000l)). // 6 months max
  setAudience ("https://appleid.apple.com").
  setSubject ("com.my.app.name").
  signWith (SignatureAlgorithm.ES256, pKey).compact ();

@jgrandja
Copy link
Contributor Author

Thanks for the sample @thomas-corte. We will be using Nimbus for implementation.

@willladislaw
Copy link

Any updates on this?

@jgrandja
Copy link
Contributor Author

@willladislaw I'm starting work on this next week.

@jgrandja
Copy link
Contributor Author

@roelal @thomas-corte @willladislaw Please see #9520.

It would be greatly appreciated if you can test out the initial implementation with the provider(s) you are using.
Please let me know if the new API provides the flexibility you require for your use case(s).

FYI, I need to get this merged before April 12 for 5.5.0-RC1, so your prompt response would be a huge help.
Thank you.

@erlendfg
Copy link

erlendfg commented Mar 30, 2021

@roelal @thomas-corte @willladislaw Please see #9520.

It would be greatly appreciated if you can test out the initial implementation with the provider(s) you are using.

I tried to download the sources from your branch and build it with Gradle, but I'm not having sufficient access to the required dependencies (Received status code 401 from server: Unauthorized), probably related to this issue: https://stackoverflow.com/questions/64839144/got-an-error-when-building-spring-security-from-source

jgrandja added a commit to jgrandja/spring-security that referenced this issue Mar 31, 2021
@Robinyo
Copy link

Robinyo commented Apr 1, 2021

@jgrandja

I would like to help out with testing support for 'Private Key JWT Client Authentication'.

The Australian Government's Trusted Digital Identity Framework requires Relying Party's to use Private Key JWT Client Authentication.

Ref:

@jgrandja
Copy link
Contributor Author

jgrandja commented Apr 5, 2021

Thanks for your offer @Robinyo. Feel free to test the implementation in #9520 with the Australian Government's Identity system.

Let me know how it goes. Thanks.

@willladislaw
Copy link

Maven repository unavailable. How do I add this dependency? (Spring Boot)

@jgrandja
Copy link
Contributor Author

@willladislaw Spring Security 5.5.0-RC1 is only available in https://repo.spring.io. After 5.5.0 goes GA then it's available in Maven Central.

See the Spring Boot reference on how to setup the Spring Maven repo.

@willladislaw
Copy link

How long before 5.5.0 GA (approx)? No other way to add it?

@erlendfg
Copy link

I'm eager to try this out, but I didn't get any further after I added the following to my config class (and removed clientSecret):
.clientAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT)

Should I customize any Spring Security class and sign the JWT by using Nimbus? In that case, in which class? Or is it possible to add the certificate along with the private key to an existing Spring Security class?

@jgrandja
Copy link
Contributor Author

@willladislaw See milestone dates

@jgrandja
Copy link
Contributor Author

@erlendfg See the main comment in gh-9520 as it points to a couple of tests that demonstrate configuration / usage.

@marcerik
Copy link

marcerik commented Jun 1, 2021

I tried out this, but did not get it to work, seems like x5t:base64EncodedThumprintHere and typ:jwt parameters inside the client_assertion are not sent to to the Authentication provider at all.

@alan-czajkowski
Copy link
Contributor

alan-czajkowski commented Jan 6, 2022

@thomas-corte do you have a working example of how to configure the application.yaml and how to use a non-static client secret using the private key?

@thomas-corte
Copy link

No, just what I wrote here:
#8175 (comment)

@alan-czajkowski
Copy link
Contributor

alan-czajkowski commented Jan 6, 2022

@thomas-corte so are you manually "fixing" the client-secret every few months?
or do you generate it on application start-up, and assume that the application will be restarted in less than 6 months, therefore generating on start-up is "good enough"?
or do you generate it somehow during application run-time and dynamically inject it to Spring Boot's OAuth2 library before it sends the request to Apple?

@thomas-corte
Copy link

No, this is not production code, I just wanted to provide a working example for generating a suitable secret to the maintainers of the related Spring code.
In our own project, we moved to using KeyCloak and its identity broker functionality (though I’m not sure whether it supports apple as an OpenID provider to be honest).

@alan-czajkowski
Copy link
Contributor

if anybody has a working Spring Boot example that dynamically generates a (non-static) "client secret" for providers such as Apple, then please reference it here

@Robinyo
Copy link

Robinyo commented Jan 9, 2022

Private Key JWT Client Authentication using nimbus-jose-jwt.

gist: https://gist.github.com/Robinyo/cc90c191be74ca173fb483199b3efceb

@alan-czajkowski
Copy link
Contributor

alan-czajkowski commented Jan 10, 2022

@Robinyo i'm looking for a solution that's compatible with this configuration:

application.yaml

spring:
  security:
    oauth2:
      client:
        registration:
          apple:
            client-name: "Apple"
            client-id: "..."

            # client secret is a signed JWT that must be auto-generated at either application start-up or during application run-time
            client-secret: "..."

            redirect-uri: "{baseUrl}/security/auth/oauth2/callback/{registrationId}"
            authorization-grant-type: "authorization_code"
            client-authentication-method: "post"
            scope:
              - "email"
              - "openid"
              - "name"
        provider:
          apple:
            authorization-uri: "https://appleid.apple.com/auth/authorize?response_mode=form_post"
            token-uri: "https://appleid.apple.com/auth/token"
            jwk-set-uri: "https://appleid.apple.com/auth/keys"
            user-name-attribute: "sub"
...

@Robinyo
Copy link

Robinyo commented Jan 10, 2022

Spring Boot and Keycloak example: application.yml

@sabareeshkkanan
Copy link

@alan-czajkowski were you able to find an working example with dynamic client secret ?

@alan-czajkowski
Copy link
Contributor

alan-czajkowski commented Mar 31, 2022

@sabareeshkkanan no, I was not able to find a working example using a dynamic client secret ... so I created my own:
https://gitlab.com/openease/java-microservice-example/-/blob/master/service/www/src/main/java/com/openease/service/www/WwwWebApplication.java

let me know if that works for you

@alexsyd
Copy link

alexsyd commented Sep 25, 2024

So, is Apple Authentication supported? Is there any documentation/examples? Where should I put the private key, teamId etc?
There is ClientAuthenticationMethod("private_key_jwt") in
https://github.com/spring-projects/spring-security/blob/6.0.x/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/ClientAuthenticationMethod.java
is it working? how do I use this type of authentication method?

@alan-czajkowski
Copy link
Contributor

alan-czajkowski commented Sep 25, 2024

@alexsyd this is an outdated example (I need to update it), but it gives you an idea of how you need to do Apple Sign-in, unfortunately Apple makes it incredibly difficult relative to everybody else (Google, Facebook, etc.)
https://gitlab.com/openease/java-microservice-example

the main thing you need to do is create the client secret (the JWT) before the Spring Boot application starts, so that you can do some magic injection into the application.yaml, and then start the application, I've documented it pretty well:
https://gitlab.com/openease/java-microservice-example/-/blob/master/service/www/src/main/java/com/openease/service/www/WwwWebApplication.java?ref_type=heads#L47

@alan-czajkowski
Copy link
Contributor

alan-czajkowski commented Sep 25, 2024

it would be helpful if Spring Boot could just implement this (painful) use-case by Apple, and do the client secret (JWT) generation, if the sign-in partner (like Apple) requires it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose) type: enhancement A general enhancement
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants