You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

141 lines
2.9 KiB

package apple_auth
import (
"context"
"crypto/rsa"
"encoding/base64"
"encoding/json"
"errors"
"io"
"math/big"
"net/http"
"strings"
"github.com/golang-jwt/jwt/v5"
)
type appleKey struct {
Kty string `json:"kty"`
Kid string `json:"kid"`
Use string `json:"use"`
Alg string `json:"alg"`
N string `json:"n"`
E string `json:"e"`
}
type appleKeyResponse struct {
Keys []appleKey `json:"keys"`
}
func newSys(options Options) (sys *Google, err error) {
sys = &Google{
options: options,
}
return
}
type Google struct {
options Options
}
func (this *Google) Auth(ctx context.Context, identityToken string) (info string, err error) {
// 获取 token header 部分
parts := strings.Split(identityToken, ".")
if len(parts) < 2 {
return "", errors.New("invalid token")
}
// 解码 header,提取 kid
headerSegment, err := base64.RawURLEncoding.DecodeString(parts[0])
if err != nil {
return "", err
}
var header map[string]interface{}
if err := json.Unmarshal(headerSegment, &header); err != nil {
return "", err
}
kid := header["kid"].(string)
// 获取 apple 公钥列表
keys, err := fetchApplePublicKeys()
if err != nil {
return "", err
}
var pubKey *rsa.PublicKey
for _, key := range keys {
if key.Kid == kid {
pubKey, err = buildRSAPublicKey(key.N, key.E)
if err != nil {
return "", err
}
break
}
}
if pubKey == nil {
return "", errors.New("public key not found")
}
// 验证 JWT
token, err := jwt.Parse(identityToken, func(t *jwt.Token) (interface{}, error) {
return pubKey, nil
})
if err != nil || !token.Valid {
return "", errors.New("invalid token")
}
claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
return "", errors.New("invalid claims")
}
// 额外校验 iss/aud(推荐做)
if claims["iss"] != "https://appleid.apple.com" {
return "", errors.New("invalid issuer")
}
// 如果你知道前端用的 client_id,也可以校验 aud:
// if claims["aud"] != "com.your.bundle.id" {
// return "", errors.New("invalid audience")
// }
sub := claims["sub"].(string) // Apple 的唯一用户 ID
return sub, nil
}
// 获取 Apple 公钥列表
func fetchApplePublicKeys() ([]appleKey, error) {
resp, err := http.Get("https://appleid.apple.com/auth/keys")
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var keyResp appleKeyResponse
if err := json.Unmarshal(body, &keyResp); err != nil {
return nil, err
}
return keyResp.Keys, nil
}
// 将 base64 N 和 E 转换成 RSA 公钥
func buildRSAPublicKey(nStr, eStr string) (*rsa.PublicKey, error) {
nBytes, err := base64.RawURLEncoding.DecodeString(nStr)
if err != nil {
return nil, err
}
eBytes, err := base64.RawURLEncoding.DecodeString(eStr)
if err != nil {
return nil, err
}
e := 0
for _, b := range eBytes {
e = e<<8 + int(b)
}
return &rsa.PublicKey{
N: new(big.Int).SetBytes(nBytes),
E: e,
}, nil
}