問題描述
我想達到的目標:
- iOS 客戶端向后端發送 JWT 令牌.
- 后端 (Java) 調用 https://appleid.apple.com/auth/token驗證令牌.
到目前為止我所擁有的:
撥打 Apple 驗證電話:
restTemplate = new RestTemplate();HttpHeaders 標頭 = 新的 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";字符串響應 = 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)//應用 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 轉換器 = 新 JcaPEMKeyConverter();最終 PrivateKeyInfo 對象 = (PrivateKeyInfo) pemParser.readObject();最終 PrivateKey pKey = converter.getPrivateKey(object);
我確認我的 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 個部分組成,用點分隔
但 authorizationCode
有 4 個部分,如下所示:
text1.text2.0.text3
您可能正在使用 iOS 應用程序中的 identityToken
而不是 authorizationCode
這是您檢索它的方式:
let authorizationCode = String(data: appleIDCredential.authorizationCode!, encoding: .utf8)!打印(授權碼:(授權碼)")
對于那些在遇到相同的 invalid_client
錯誤后可能來到這里的人來說,記住以下幾點也很好:
kid 是 developer.apple.com/account/resources/authkeys/list 中私鑰的 ID
keyFile 是保存從 developer.apple.com 下載的私鑰的文件
登錄developer.apple.com點擊賬號可以找到teamID,右上角可以看到teamID
aud 中的值應該是https://appleid.apple.com
app_id 是應用程序的包標識符
如果它可能有幫助,這里有一個在 python 中創建 client_secret 的可行解決方案:
# $ pip install pyjwt導入 jwt進口時間孩子=myKeyId"keyFile = "/pathToFile/AuthKey.p8";鍵="使用 open(keyFile, 'r') 作為 myFile:鍵 = myFile.read()打印(鍵)timeNow = int(round(time.time()))time3Months = timeNow + 86400*90索賠= {'iss':團隊ID,'iat':時間現在,'exp': time3Months,'aud': 'https://appleid.apple.com',子":app_id,}秘密= jwt.encode(聲明,密鑰,算法='ES256',標題={'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)
這篇關于invalid_client 用于使用蘋果登錄的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!