Livio Spring e17b49e4ca
feat: add apple as idp (#6442)
* feat: manage apple idp

* handle apple idp callback

* add tests for provider

* basic console implementation

* implement flow for login UI and add logos / styling

* tests

* cleanup

* add upload button

* begin i18n

* apple logo positioning, file upload component

* fix add apple instance idp

* add missing apple logos for login

* update to go 1.21

* fix slice compare

* revert permission changes

* concrete error messages

* translate login apple logo -y-2px

* change form parsing

* sign in button

* fix tests

* lint console

---------

Co-authored-by: peintnermax <max@caos.ch>
2023-08-31 08:39:16 +02:00

69 lines
1.8 KiB
Go

package apple
import (
"crypto/x509"
"encoding/pem"
"time"
"github.com/zitadel/oidc/v2/pkg/crypto"
openid "github.com/zitadel/oidc/v2/pkg/oidc"
"gopkg.in/square/go-jose.v2"
"github.com/zitadel/zitadel/internal/idp"
"github.com/zitadel/zitadel/internal/idp/providers/oidc"
)
const (
name = "Apple"
issuer = "https://appleid.apple.com"
)
var _ idp.Provider = (*Provider)(nil)
// Provider is the [idp.Provider] implementation for Apple
type Provider struct {
*oidc.Provider
}
func New(clientID, teamID, keyID, callbackURL string, key []byte, scopes []string, options ...oidc.ProviderOpts) (*Provider, error) {
secret, err := clientSecretFromPrivateKey(key, teamID, clientID, keyID)
if err != nil {
return nil, err
}
options = append(options, oidc.WithResponseMode("form_post"))
rp, err := oidc.New(name, issuer, clientID, secret, callbackURL, scopes, oidc.DefaultMapper, options...)
if err != nil {
return nil, err
}
return &Provider{
Provider: rp,
}, nil
}
// clientSecretFromPrivateKey uses the private key to create and sign a JWT, which has to be used as client_secret at Apple.
func clientSecretFromPrivateKey(key []byte, teamID, clientID, keyID string) (string, error) {
block, _ := pem.Decode(key)
b := block.Bytes
pk, err := x509.ParsePKCS8PrivateKey(b)
if err != nil {
return "", err
}
signingKey := jose.SigningKey{
Algorithm: jose.ES256,
Key: &jose.JSONWebKey{Key: pk, KeyID: keyID},
}
signer, err := jose.NewSigner(signingKey, &jose.SignerOptions{})
if err != nil {
return "", err
}
iat := time.Now()
exp := iat.Add(time.Hour)
return crypto.Sign(&openid.JWTTokenRequest{
Issuer: teamID,
Subject: clientID,
Audience: []string{issuer},
ExpiresAt: openid.FromTime(exp),
IssuedAt: openid.FromTime(iat),
}, signer)
}