0

We have begun implementing Sign in with Apple in our mobile app but cannot seem to call the token endpoint successfully (https://appleid.apple.com/auth/token). The response we get is 400 with the body [{"error":"invalid_client"}]. I have read and re-read the details on how to generate the client-secret. We are using a java backend and specifically the nimbus library to create the signed JWT.

final JWSHeader clientSecretHeader = 
  new JWSHeader.Builder(JWSAlgorithm.ES256)  
    .keyID("7N5XJ*****")  
    .build();  
final Date issuedAtTime = Date.from(Instant.now());  
final Date expirationTime = Date.from(Instant.now().plusSeconds(3600));  
final JWTClaimsSet clientSecretClaims =   
  new JWTClaimsSet.Builder()  
   .issuer("HL46P*****")  
   .issueTime(issuedAtTime)  
   .expirationTime(expirationTime)  
   .audience("https://appleid.apple.com")  
   .subject("com.company.app")  
   .build();  
final byte[] keyBytes = ...private key loaded from p8 file...;  
final KeyFactory keyFactory = KeyFactory.getInstance("EC");
final ECPrivateKey privateKey =
  (ECPrivateKey)keyFactory.generatePrivate(new PKCS8EncodedKeySpec(keyBytes));
final JWSSigner jwtSigner = new ECDSASigner(signingKey);  
final SignedJWT clientSecretJwt = new SignedJWT(clientSecretHeader, clientSecretClaims);  
clientSecretJwt.sign(jwtSigner);

final MultiValueMap<string, string=""> map= new LinkedMultiValueMap<>();  
map.add("grant_type", "authorization_code");  
map.add("client_id", "HL46P*****");  
map.add("client_secret", clientSecretJwt.serialize());  
map.add("code", "code receiged from app...");

/* if User-Agent is not set, request fails with 400 invalid_client */
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.set(HttpHeaders.USER_AGENT, "My App");

final HttpEntity<multivaluemap<string, string="">> request = new HttpEntity<>(map, headers);  
final RestTemplate restTemplate = new RestTemplateBuilder().build();  
final ResponseEntity response = response = restTemplate.postForEntity(  
  "https://appleid.apple.com/auth/token",  
  request,  
  GetTokenResponse.class  
 );  

The resulting JWT looks like the following:

Header

{  
  "kid": "7N5XJ*****",  
  "alg": "ES256"  
}

Claims

{  
  "aud": "https://appleid.apple.com",  
  "sub": "com.company.app",  
  "iss": "HL46P.....",  
  "exp": 1585583898,  
  "iat": 1585580298  
}

In the end, we determined that if the User-Agent header is not set, the apple API will fail with a 400 response code and invalid_client as the error. That error is completely misleading, but setting the User-Agent header resolves it.

Mike
  • 1,031
  • 14
  • 36
  • Take a look at my answer, I also had lots of problems with invalid_client, maybe it will help you: https://stackoverflow.com/a/59242658/1099716 – Access Denied Mar 31 '20 at 10:02
  • Start with `curl` and make it work. I don't think it's java specific problem./ – Access Denied Mar 31 '20 at 10:04
  • 1
    It would seem that the User-Agent header was the missing piece. Added that and now the calls are successful. Thanks @AccessDenied – Mike Mar 31 '20 at 12:52
  • I have updated my question with the fix, hopefully this working version will save someone else the hours I spent trying to fix my invalid client when it was correct from the beginning – Mike Mar 31 '20 at 14:29

0 Answers0