invalid_client 用于使用苹果登录

invalid_client for sign in with apple

我努力实现的目标:

我目前拥有的:

拨打 Apple 验证电话:

        restTemplate = new RestTemplate();

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
        map.add("client_id", clientId); // app_id like com.app.id
        String token = generateJWT();   // generated jwt
        map.add("client_secret", token); 
        map.add("grant_type", "authorization_code");
        map.add("code", authorizationCode);  // JWT code we got from iOS
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);

        final String appleAuthURL = "https://appleid.apple.com/auth/token";
        String response = restTemplate.postForObject(appleAuthURL, request, String.class);

令牌生成:

        final PrivateKey privateKey = getPrivateKey();
        final int expiration = 1000 * 60 * 5;

        String token = Jwts.builder()
                .setHeaderParam(JwsHeader.KEY_ID, keyId) // key id I got from Apple 
                .setIssuer(teamId)  
                .setAudience("https://appleid.apple.com")
                .setSubject(clientId) // app id com.app.id
                .setExpiration(new Date(System.currentTimeMillis() + expiration))
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .signWith(SignatureAlgorithm.ES256, privateKey) // ECDSA using P-256 and SHA-256
                .compact();

        return token;

从文件中获取我的私钥:

        final Reader pemReader = new StringReader(getKeyData());
        final PEMParser pemParser = new PEMParser(pemReader);
        final JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
        final PrivateKeyInfo object = (PrivateKeyInfo) pemParser.readObject();
        final PrivateKey pKey = converter.getPrivateKey(object);

我确认我的 JWT 具有所有必填字段:

{
  "kid": "SAME KEY AS MY KEY ID",
  "alg": "ES256"
}

{
  "iss": "Blahblah",
  "aud": "https://appleid.apple.com",
  "sub": "com.app.id",
  "exp": 1578513833,
  "iat": 1578513533
}

这一行引起了我的注意:

map.add("code", authorizationCode);  // JWT code we got from iOS

authorizationCode 不是 jwt

JSON Web Tokens 由 3 个部分组成,用点分隔

但是 authorizationCode 有 4 个部分,如下所示:

text1.text2.0.text3

您可能正在使用 iOS 应用中的 identityToken 而不是 authorizationCode

这是您检索它的方式:

let authorizationCode = String(data: appleIDCredential.authorizationCode!, encoding: .utf8)!
print("authorizationCode: \(authorizationCode)")

对于那些在遇到相同的 invalid_client 错误后可能会来到这里的人来说,记住以下几点也很好:

  1. kid is the id for the private key from developer.apple.com/account/resources/authkeys/list

  2. keyFile is the file holding the private key downloaded from developer.apple.com

  3. teamID can be found by logging in to developer.apple.com and clicking on account, the teamID can be seen in the upper right corner

  4. the value in aud should be https://appleid.apple.com

  5. app_id is the bundle identifier for the app

万一它可能有帮助,这里是 python 中的一个工作解决方案,用于创建 client_secret:

# $ pip install pyjwt
import jwt
import time

kid = "myKeyId"  
keyFile = "/pathToFile/AuthKey.p8"
key = ""
with open(keyFile, 'r') as myFile:
    key = myFile.read()

print(key)

timeNow = int(round(time.time()))
time3Months = timeNow + 86400*90

claims = {
    'iss': teamID,
    'iat': timeNow,
    'exp': time3Months,
    'aud': 'https://appleid.apple.com',
    'sub': app_id,
}


secret = jwt.encode(claims, key, algorithm='ES256', headers={'kid': kid})
print("secret:")
print(secret)
client_secret = secret.decode("utf-8")
print(client_secret)