I am trying to test the machine to machine okta authentication for a backend service.I created a service app using Okta inegrator account for the REST API endpoint that I need to authenticate with Okta.I chose Public /Private key for client authentication and generated the public/private keys from okta admin console.Now when am testing the flow by writing a standalone springboot web app and calling the /token endpoint am getting 400 bad request invalid client.I have verified the signed JWT by decoding the JWT and everything looks good.I have added the default access policy as well in okta admin console and selected this service app under assigned to clients but still does not work.Any inputs or suggestions please to make this work?The KID,client ID,sub and aud everything looks good.Not sure where am going wrong.Please help.
I am using Spring Rest Template to make the http POST call to /token endpoint to get the access token.
Are you getting a more specific error description back beyond invalid_client? Usually a more helpful error will be returned, for example, telling you if something in the request is formatted incorrectly or that the assertion has expired.
Thanks for the response.I do not get any additional infomrmtion back from okta while calling the token endpoint.Is there anything I need to add to get more info from the /token endpoint?
I have configured service app in okta admin console using API service option as I want to set up machine to machine okta auth.I have selected public/private key and not client credentials for public keys.I have generated the key pair in okta admin console and I got the private key in JWK format.Now keeping that private key in my client that is springboot web app I created a signed JWT (assertion) and trying to make a call to okta /token endpoint to get an access token so that I can get authenticated.Hence I chose JWT bearer token as grant type and not client credentials.Please correct me if am wrong.
When you select Public key / Private key instead of Client secret for Client authentication field, you are changing the authentication not the grant type. Grant types will have Client Credentials and Token Exchange.
For your use case as described, you might have to use client_credentials grant type as I stated in my previous response. My example request accommodates you choosing Public key / Private key by adding client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearerheader.
You are receiving invalid client error due to the fact that your token call does not include the right authentication which will contain the client ID.
I tried with client_credentials but still same issue..Please find the error org.springframework.web.client.HttpClientErrorException$BadRequest: 400 Bad Request on POST request for âhttps://integrator-XXXXXX.okta.com/oauth2/default/v1/tokenâ: â{âerrorCodeâ:âinvalid_clientâ,âerrorSummaryâ:âA client_id must be provided in the request.â,âerrorLinkâ:âinvalid_clientâ,âerrorIdâ:âoaeCx_lJfNTQYiY6PmzfEOAJwâ,âerrorCausesâ:}â..change I made is MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add(âgrant_typeâ, âurn:ietf:params:oauth:grant-type:client_credentialsâ);
body.add(âassertionâ, assertion);
body.add(âscopeâ, âopenidâ);
What does the payload for your assertion look like?
Also, did you generate a new JWT (to use as the assertion) for this request, or are you potentially reusing a previous one? If you havenât, you will want to try generating a new assertion immediately before sending it to the /token endpoint
I just tried with grant_type as client_credentials but still same..And every time am generating new JWT..mean in claims i set issuetime and exp timeâŚbelow is my code for creating signed JWT
private String createSignedJwt() throws Exception {
System.out.println(clientId);
RSAPrivateKey privateKey = loadPrivateKey();
RSAKey rsaKey = RSAKey.parse(new String(
new ClassPathResource(privateKeyPath).getInputStream().readAllBytes()
));
System.out.println("kid from JWK = " + rsaKey.getKeyID());
System.out.println("clientId = " + clientId);
System.out.println("audience = " + tokenUrl);
JWSSigner signer = new RSASSASigner(privateKey);
JWTClaimsSet claims = new JWTClaimsSet.Builder()
.issuer(clientId)
.subject(clientId)
.audience(tokenUrl)
.issueTime(new Date())
.expirationTime(new Date(System.currentTimeMillis() + 5 * 60 * 1000))
.jwtID(UUID.randomUUID().toString())
.build();
SignedJWT signedJWT = new SignedJWT(
new JWSHeader.Builder(JWSAlgorithm.RS256)
.keyID("XXXXXXX")
.build(),
claims
);
signedJWT.sign(signer);
return signedJWT.serialize();
}--have redacted KID for security..and updated headers MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("grant_type", "client_credentials");
body.add("assertion", assertion);
body.add("scope", "openid");
and am loading provate key from json file
private RSAPrivateKey loadPrivateKey() throws Exception {
//String jwkJson = Files.readString(Paths.get(privateKeyPath));
ClassPathResource resource = new ClassPathResource("okta-private-key.json");
String jwkJson = new String(resource.getInputStream().readAllBytes());
RSAKey rsaKey = RSAKey.parse(jwkJson);
return rsaKey.toRSAPrivateKey();
}
Apparently I had to set client_id in the request body.After that earlier error is gone but now am getting â{âerrorâ:âinvalid_dpop_proofâ,âerror_descriptionâ:âThe DPoP proof JWT header is missing.â}ââŚno idea what this means..any inputs please?
Somehow as per googling figured out had to disable DPoP option in okta admin console for the service app.After having done that now am getting invalid_scope {âerrorâ:âinvalid_scopeâ,âerror_descriptionâ:âOne or more scopes are not configured for the authorization server resource.â}"âŚtried with body.add(âscopeâ, âokta.users.readâ); still same issue..appraently when I check âAdmin Rolesâ tab for the tab there are no admin roles assigned and I believe for free integrator account we cannot assign admin roles to apps..so what are my options now to test this flow?Also for okta.users.read under Okta API scopes grant is enabled
SUCCESSS!!!Had to create a custom scope in service app api.read and grant access for this app okta.read.users cannot be used as scope for default authorization server.Thanks everyone.