mirror of
https://github.com/zitadel/zitadel.git
synced 2024-12-12 11:04:25 +00:00
48f9815b7c
The login uses the new template based IDPs with backwards compatibility for old IDPs
124 lines
3.0 KiB
Go
124 lines
3.0 KiB
Go
package jwt
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/zitadel/logging"
|
|
"github.com/zitadel/oidc/v2/pkg/client/rp"
|
|
"github.com/zitadel/oidc/v2/pkg/oidc"
|
|
"golang.org/x/text/language"
|
|
|
|
"github.com/zitadel/zitadel/internal/idp"
|
|
)
|
|
|
|
var _ idp.Session = (*Session)(nil)
|
|
|
|
var (
|
|
ErrNoTokens = errors.New("no tokens provided")
|
|
ErrInvalidToken = errors.New("invalid tokens provided")
|
|
)
|
|
|
|
// Session is the [idp.Session] implementation for the JWT provider
|
|
type Session struct {
|
|
*Provider
|
|
AuthURL string
|
|
Tokens *oidc.Tokens
|
|
}
|
|
|
|
// GetAuthURL implements the [idp.Session] interface
|
|
func (s *Session) GetAuthURL() string {
|
|
return s.AuthURL
|
|
}
|
|
|
|
// FetchUser implements the [idp.Session] interface.
|
|
// It will map the received idToken into an [idp.User].
|
|
func (s *Session) FetchUser(ctx context.Context) (user idp.User, err error) {
|
|
if s.Tokens == nil {
|
|
return nil, ErrNoTokens
|
|
}
|
|
s.Tokens.IDTokenClaims, err = s.validateToken(ctx, s.Tokens.IDToken)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &User{s.Tokens.IDTokenClaims}, nil
|
|
}
|
|
|
|
func (s *Session) validateToken(ctx context.Context, token string) (oidc.IDTokenClaims, error) {
|
|
logging.Debug("begin token validation")
|
|
// TODO: be able to specify them in the template: https://github.com/zitadel/zitadel/issues/5322
|
|
offset := 3 * time.Second
|
|
maxAge := time.Hour
|
|
claims := oidc.EmptyIDTokenClaims()
|
|
payload, err := oidc.ParseToken(token, claims)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: malformed jwt payload: %v", ErrInvalidToken, err)
|
|
}
|
|
|
|
if err = oidc.CheckIssuer(claims, s.Provider.issuer); err != nil {
|
|
return nil, fmt.Errorf("%w: invalid issuer: %v", ErrInvalidToken, err)
|
|
}
|
|
|
|
logging.Debug("begin signature validation")
|
|
keySet := rp.NewRemoteKeySet(http.DefaultClient, s.Provider.keysEndpoint)
|
|
if err = oidc.CheckSignature(ctx, token, payload, claims, nil, keySet); err != nil {
|
|
return nil, fmt.Errorf("%w: invalid signature: %v", ErrInvalidToken, err)
|
|
}
|
|
|
|
if !claims.GetExpiration().IsZero() {
|
|
if err = oidc.CheckExpiration(claims, offset); err != nil {
|
|
return nil, fmt.Errorf("%w: expired: %v", ErrInvalidToken, err)
|
|
}
|
|
}
|
|
|
|
if !claims.GetIssuedAt().IsZero() {
|
|
if err = oidc.CheckIssuedAt(claims, maxAge, offset); err != nil {
|
|
return nil, fmt.Errorf("%w: %v", ErrInvalidToken, err)
|
|
}
|
|
}
|
|
return claims, nil
|
|
}
|
|
|
|
type User struct {
|
|
oidc.IDTokenClaims
|
|
}
|
|
|
|
func (u *User) GetID() string {
|
|
return u.IDTokenClaims.GetSubject()
|
|
}
|
|
|
|
func (u *User) GetFirstName() string {
|
|
return u.IDTokenClaims.GetGivenName()
|
|
}
|
|
|
|
func (u *User) GetLastName() string {
|
|
return u.IDTokenClaims.GetFamilyName()
|
|
}
|
|
|
|
func (u *User) GetDisplayName() string {
|
|
return u.IDTokenClaims.GetName()
|
|
}
|
|
|
|
func (u *User) GetNickname() string {
|
|
return u.IDTokenClaims.GetNickname()
|
|
}
|
|
|
|
func (u *User) GetPhone() string {
|
|
return u.IDTokenClaims.GetPhoneNumber()
|
|
}
|
|
|
|
func (u *User) IsPhoneVerified() bool {
|
|
return u.IDTokenClaims.IsPhoneNumberVerified()
|
|
}
|
|
|
|
func (u *User) GetPreferredLanguage() language.Tag {
|
|
return u.IDTokenClaims.GetLocale()
|
|
}
|
|
|
|
func (u *User) GetAvatarURL() string {
|
|
return u.IDTokenClaims.GetPicture()
|
|
}
|