86 lines
2.0 KiB
Go
86 lines
2.0 KiB
Go
package rpc
|
|
|
|
import (
|
|
"context"
|
|
"crypto/ecdsa"
|
|
"crypto/x509"
|
|
"encoding/pem"
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/Timothylock/go-signin-with-apple/apple"
|
|
"github.com/golang-jwt/jwt/v5"
|
|
)
|
|
|
|
type AppleClient struct {
|
|
*apple.Client
|
|
teamID string
|
|
clientID string
|
|
keyID string
|
|
secret string
|
|
}
|
|
|
|
// GenerateClientSecret generates a new client secret, all fields are required, no http request, fully local logic
|
|
func (a *AppleClient) GenerateClientSecret() (string, error) {
|
|
token := &jwt.Token{
|
|
Header: map[string]interface{}{
|
|
"alg": "ES256",
|
|
"kid": a.keyID,
|
|
},
|
|
Claims: jwt.MapClaims{
|
|
"iss": a.teamID,
|
|
"iat": time.Now().Unix(),
|
|
// constraint: exp - iat <= 180 days
|
|
"exp": time.Now().Add(24 * time.Hour).Unix(),
|
|
"aud": "https://appleid.apple.com",
|
|
"sub": a.clientID,
|
|
},
|
|
Method: jwt.SigningMethodES256,
|
|
}
|
|
|
|
ecdsaKey, _ := authKeyFromBytes([]byte(a.secret))
|
|
return token.SignedString(ecdsaKey)
|
|
}
|
|
|
|
// authKeyFromBytes create private key for jwt sign
|
|
func authKeyFromBytes(key []byte) (*ecdsa.PrivateKey, error) {
|
|
var err error
|
|
|
|
var block *pem.Block
|
|
if block, _ = pem.Decode(key); block == nil {
|
|
return nil, errors.New("token: AuthKey must be a valid .p8 PEM file")
|
|
}
|
|
|
|
var parsedKey interface{}
|
|
if parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var pkey *ecdsa.PrivateKey
|
|
var ok bool
|
|
if pkey, ok = parsedKey.(*ecdsa.PrivateKey); !ok {
|
|
return nil, errors.New("token: AuthKey must be of type ecdsa.PrivateKey")
|
|
}
|
|
|
|
return pkey, nil
|
|
}
|
|
|
|
func (a *AppleClient) VerifyAppToken(ctx context.Context, code string) (apple.ValidationResponse, error) {
|
|
var resp apple.ValidationResponse
|
|
clientSecret, err := a.GenerateClientSecret()
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
|
|
reqBody := apple.AppValidationTokenRequest{
|
|
ClientID: a.clientID,
|
|
ClientSecret: clientSecret,
|
|
Code: code,
|
|
}
|
|
if err := a.Client.VerifyAppToken(ctx, reqBody, &resp); err != nil {
|
|
return resp, err
|
|
}
|
|
|
|
return resp, nil
|
|
}
|