mirror of
https://github.com/zitadel/zitadel.git
synced 2024-12-24 00:28:18 +00:00
f99cf50f69
add authURL parameters to urls for external IDPs, depended on the contents of the authRequest --------- Co-authored-by: Livio Spring <livio.a@gmail.com>
136 lines
3.4 KiB
Go
136 lines
3.4 KiB
Go
package jwt
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"errors"
|
|
"net/url"
|
|
|
|
"github.com/zitadel/zitadel/internal/crypto"
|
|
"github.com/zitadel/zitadel/internal/idp"
|
|
)
|
|
|
|
const (
|
|
queryAuthRequestID = "authRequestID"
|
|
queryUserAgentID = "userAgentID"
|
|
)
|
|
|
|
var _ idp.Provider = (*Provider)(nil)
|
|
|
|
var (
|
|
ErrMissingUserAgentID = errors.New("userAgentID missing")
|
|
)
|
|
|
|
// Provider is the [idp.Provider] implementation for a JWT provider
|
|
type Provider struct {
|
|
name string
|
|
headerName string
|
|
issuer string
|
|
jwtEndpoint string
|
|
keysEndpoint string
|
|
isLinkingAllowed bool
|
|
isCreationAllowed bool
|
|
isAutoCreation bool
|
|
isAutoUpdate bool
|
|
encryptionAlg crypto.EncryptionAlgorithm
|
|
}
|
|
|
|
type ProviderOpts func(provider *Provider)
|
|
|
|
// WithLinkingAllowed allows end users to link the federated user to an existing one
|
|
func WithLinkingAllowed() ProviderOpts {
|
|
return func(p *Provider) {
|
|
p.isLinkingAllowed = true
|
|
}
|
|
}
|
|
|
|
// WithCreationAllowed allows end users to create a new user using the federated information
|
|
func WithCreationAllowed() ProviderOpts {
|
|
return func(p *Provider) {
|
|
p.isCreationAllowed = true
|
|
}
|
|
}
|
|
|
|
// WithAutoCreation enables that federated users are automatically created if not already existing
|
|
func WithAutoCreation() ProviderOpts {
|
|
return func(p *Provider) {
|
|
p.isAutoCreation = true
|
|
}
|
|
}
|
|
|
|
// WithAutoUpdate enables that information retrieved from the provider is automatically used to update
|
|
// the existing user on each authentication
|
|
func WithAutoUpdate() ProviderOpts {
|
|
return func(p *Provider) {
|
|
p.isAutoUpdate = true
|
|
}
|
|
}
|
|
|
|
// New creates a JWT provider
|
|
func New(name, issuer, jwtEndpoint, keysEndpoint, headerName string, encryptionAlg crypto.EncryptionAlgorithm, options ...ProviderOpts) (*Provider, error) {
|
|
provider := &Provider{
|
|
name: name,
|
|
issuer: issuer,
|
|
jwtEndpoint: jwtEndpoint,
|
|
keysEndpoint: keysEndpoint,
|
|
headerName: headerName,
|
|
encryptionAlg: encryptionAlg,
|
|
}
|
|
for _, option := range options {
|
|
option(provider)
|
|
}
|
|
|
|
return provider, nil
|
|
}
|
|
|
|
// Name implements the [idp.Provider] interface
|
|
func (p *Provider) Name() string {
|
|
return p.name
|
|
}
|
|
|
|
// BeginAuth implements the [idp.Provider] interface.
|
|
// It will create a [Session] with an AuthURL, pointing to the jwtEndpoint
|
|
// with the authRequest and encrypted userAgent ids.
|
|
func (p *Provider) BeginAuth(ctx context.Context, state string, params ...any) (idp.Session, error) {
|
|
if len(params) < 1 {
|
|
return nil, ErrMissingUserAgentID
|
|
}
|
|
userAgentID, ok := params[0].(string)
|
|
if !ok {
|
|
return nil, ErrMissingUserAgentID
|
|
}
|
|
redirect, err := url.Parse(p.jwtEndpoint)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
q := redirect.Query()
|
|
q.Set(queryAuthRequestID, state)
|
|
nonce, err := p.encryptionAlg.Encrypt([]byte(userAgentID))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
q.Set(queryUserAgentID, base64.RawURLEncoding.EncodeToString(nonce))
|
|
redirect.RawQuery = q.Encode()
|
|
return &Session{AuthURL: redirect.String()}, nil
|
|
}
|
|
|
|
// IsLinkingAllowed implements the [idp.Provider] interface.
|
|
func (p *Provider) IsLinkingAllowed() bool {
|
|
return p.isLinkingAllowed
|
|
}
|
|
|
|
// IsCreationAllowed implements the [idp.Provider] interface.
|
|
func (p *Provider) IsCreationAllowed() bool {
|
|
return p.isCreationAllowed
|
|
}
|
|
|
|
// IsAutoCreation implements the [idp.Provider] interface.
|
|
func (p *Provider) IsAutoCreation() bool {
|
|
return p.isAutoCreation
|
|
}
|
|
|
|
// IsAutoUpdate implements the [idp.Provider] interface.
|
|
func (p *Provider) IsAutoUpdate() bool {
|
|
return p.isAutoUpdate
|
|
}
|