invalid_client для входа с яблоком - PullRequest
3 голосов
/ 09 января 2020

Чего я пытаюсь достичь:

  • iOS клиент отправляет токен JWT на бэкэнд.
  • Бэкэнд (Java) звонки https://appleid.apple.com/auth/token для проверки токена.

что у меня есть на данный момент:

, чтобы сделать проверочный вызов 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
}

1 Ответ

3 голосов
/ 23 января 2020

Эта строка привлекла мое внимание:

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

authorizationCode не является jwt

JSON Веб-токены состоят из 3 частей, разделенных точками

, но authorizationCode состоит из 4-х частей:

text1.text2.0.text3

Вы, вероятно, используете identityToken из приложения iOS вместо authorizationCode

Это как вы его извлекаете:

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

Также полезно иметь в виду следующее для тех, кто может прийти сюда после получения той же invalid_client ошибки:

  1. kid - это идентификатор закрытого ключа от developer.apple.com/account/resources/authkeys/list

  2. keyFile - это файл, содержащий закрытый ключ, загруженный из developer.apple. com

  3. teamID можно найти, войдя на developer.apple.com и нажав на учетную запись, teamID можно увидеть в верхнем правом углу

  4. значение в ауд. Должно быть https://appleid.apple.com

  5. app_id - идентификатор пакета для приложения

Если это может помочь, вот рабочее решение в 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)
...