問題描述
我想達(dá)到的目標(biāo):
- iOS 客戶端向后端發(fā)送 JWT 令牌.
- 后端 (Java) 調(diào)用 https://appleid.apple.com/auth/token驗(yàn)證令牌.
到目前為止我所擁有的:
撥打 Apple 驗(yàn)證電話:
restTemplate = new RestTemplate();HttpHeaders 標(biāo)頭 = 新的 HttpHeaders();headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);多值映射<字符串,字符串>map = new LinkedMultiValueMap<>();map.add("client_id", clientId);//app_id 像 com.app.id字符串令牌 = generateJWT();//生成的 jwtmap.add("client_secret", token);map.add("grant_type", "authorization_code");map.add("code", authorizationCode);//我們從 iOS 獲得的 JWT 代碼HttpEntity<MultiValueMap<字符串,字符串>>request = new HttpEntity<>(map, headers);最終字符串 appleAuthURL = "https://appleid.apple.com/auth/token";字符串響應(yīng) = restTemplate.postForObject(appleAuthURL, request, String.class);
代幣生成:
final PrivateKey privateKey = getPrivateKey();最終 int 到期 = 1000 * 60 * 5;字符串令牌 = Jwts.builder().setHeaderParam(JwsHeader.KEY_ID, keyId)//我從 Apple 獲得的密鑰 id.setIssuer(teamId).setAudience("https://appleid.apple.com").setSubject(clientId)//應(yīng)用 id com.app.id.setExpiration(new Date(System.currentTimeMillis() + expire)).setIssuedAt(新日期(System.currentTimeMillis())).signWith(SignatureAlgorithm.ES256, privateKey)//ECDSA 使用 P-256 和 SHA-256.袖珍的();返回令牌;
從文件中獲取我的私鑰:
final Reader pemReader = new StringReader(getKeyData());最終 PEMParser pemParser = 新 PEMParser(pemReader);最終 JcaPEMKeyConverter 轉(zhuǎn)換器 = 新 JcaPEMKeyConverter();最終 PrivateKeyInfo 對(duì)象 = (PrivateKeyInfo) pemParser.readObject();最終 PrivateKey pKey = converter.getPrivateKey(object);
我確認(rèn)我的 JWT 具有所有必填字段:
<代碼>{"kid": "與我的鑰匙 ID 相同的鑰匙",alg":ES256"}{"iss": "廢話","aud": "https://appleid.apple.com",子":com.app.id",exp":1578513833,iat":1578513533}
這行引起了我的注意:
map.add("code", authorizationCode);//我們從 iOS 獲得的 JWT 代碼
authorizationCode
不是 jwt
JSON Web Tokens 由 3 個(gè)部分組成,用點(diǎn)分隔
但 authorizationCode
有 4 個(gè)部分,如下所示:
text1.text2.0.text3
您可能正在使用 iOS 應(yīng)用程序中的 identityToken
而不是 authorizationCode
這是您檢索它的方式:
let authorizationCode = String(data: appleIDCredential.authorizationCode!, encoding: .utf8)!打印(授權(quán)碼:(授權(quán)碼)")
對(duì)于那些在遇到相同的 invalid_client
錯(cuò)誤后可能來到這里的人來說,記住以下幾點(diǎn)也很好:
kid 是 developer.apple.com/account/resources/authkeys/list 中私鑰的 ID
keyFile 是保存從 developer.apple.com 下載的私鑰的文件
登錄developer.apple.com點(diǎn)擊賬號(hào)可以找到teamID,右上角可以看到teamID
aud 中的值應(yīng)該是https://appleid.apple.com
app_id 是應(yīng)用程序的包標(biāo)識(shí)符
如果它可能有幫助,這里有一個(gè)在 python 中創(chuàng)建 client_secret 的可行解決方案:
# $ pip install pyjwt導(dǎo)入 jwt進(jìn)口時(shí)間孩子=myKeyId"keyFile = "/pathToFile/AuthKey.p8";鍵="使用 open(keyFile, 'r') 作為 myFile:鍵 = myFile.read()打印(鍵)timeNow = int(round(time.time()))time3Months = timeNow + 86400*90索賠= {'iss':團(tuán)隊(duì)ID,'iat':時(shí)間現(xiàn)在,'exp': time3Months,'aud': 'https://appleid.apple.com',子":app_id,}秘密= jwt.encode(聲明,密鑰,算法='ES256',標(biāo)題={'kid':kid})打印(秘密:")打印(秘密)client_secret = secret.decode("utf-8")打印(client_secret)
What I try to achieve:
- iOS client sends a JWT token to the backend.
- Backend (Java) calls https://appleid.apple.com/auth/token to verify the token.
what I have so far:
to make Apple verification call:
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);
token generation:
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;
to get my private key from the file:
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);
I confirmed my JWT has all required fields:
{
"kid": "SAME KEY AS MY KEY ID",
"alg": "ES256"
}
{
"iss": "Blahblah",
"aud": "https://appleid.apple.com",
"sub": "com.app.id",
"exp": 1578513833,
"iat": 1578513533
}
This line caught my attention:
map.add("code", authorizationCode); // JWT code we got from iOS
The authorizationCode
is not a jwt
JSON Web Tokens consist of 3 parts separated by dots
but the authorizationCode
has 4 parts like this:
text1.text2.0.text3
You are probably using the identityToken
from the iOS app instead of the authorizationCode
This is how you retrieve it:
let authorizationCode = String(data: appleIDCredential.authorizationCode!, encoding: .utf8)!
print("authorizationCode: (authorizationCode)")
Also good to have the following in mind for those who might come here after getting the same invalid_client
error:
kid is the id for the private key from developer.apple.com/account/resources/authkeys/list
keyFile is the file holding the private key downloaded from developer.apple.com
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
the value in aud should be https://appleid.apple.com
app_id is the bundle identifier for the app
In case it might help, here is a working solution in python to create a 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)
這篇關(guān)于invalid_client 用于使用蘋果登錄的文章就介紹到這了,希望我們推薦的答案對(duì)大家有所幫助,也希望大家多多支持html5模板網(wǎng)!