mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 01:37:31 +00:00
feat: port reduction (#323)
* move mgmt pkg * begin package restructure * rename auth package to authz * begin start api * move auth * move admin * fix merge * configs and interceptors * interceptor * revert generate-grpc.sh * some cleanups * console * move console * fix tests and merging * js linting * merge * merging and configs * change k8s base to current ports * fixes * cleanup * regenerate proto * remove unnecessary whitespace * missing param * go mod tidy * fix merging * move login pkg * cleanup * move api pkgs again * fix pkg naming * fix generate-static.sh for login * update workflow * fixes * logging * remove duplicate * comment for optional gateway interfaces * regenerate protos * fix proto imports for grpc web * protos * grpc web generate * grpc web generate * fix changes * add translation interceptor * fix merging * regenerate mgmt proto
This commit is contained in:
82
internal/api/oidc/auth_request.go
Normal file
82
internal/api/oidc/auth_request.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package oidc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/caos/oidc/pkg/oidc"
|
||||
"github.com/caos/oidc/pkg/op"
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
)
|
||||
|
||||
func (o *OPStorage) CreateAuthRequest(ctx context.Context, req *oidc.AuthRequest, userID string) (op.AuthRequest, error) {
|
||||
userAgentID, ok := UserAgentIDFromCtx(ctx)
|
||||
if !ok {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "OIDC-sd436", "no user agent id")
|
||||
}
|
||||
authRequest := CreateAuthRequestToBusiness(ctx, req, userAgentID, userID)
|
||||
resp, err := o.repo.CreateAuthRequest(ctx, authRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return AuthRequestFromBusiness(resp)
|
||||
}
|
||||
|
||||
func (o *OPStorage) AuthRequestByID(ctx context.Context, id string) (op.AuthRequest, error) {
|
||||
resp, err := o.repo.AuthRequestByIDCheckLoggedIn(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return AuthRequestFromBusiness(resp)
|
||||
}
|
||||
|
||||
func (o *OPStorage) AuthRequestByCode(ctx context.Context, code string) (op.AuthRequest, error) {
|
||||
resp, err := o.repo.AuthRequestByCode(ctx, code)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return AuthRequestFromBusiness(resp)
|
||||
}
|
||||
|
||||
func (o *OPStorage) SaveAuthCode(ctx context.Context, id, code string) error {
|
||||
return o.repo.SaveAuthCode(ctx, id, code)
|
||||
}
|
||||
|
||||
func (o *OPStorage) DeleteAuthRequest(ctx context.Context, id string) error {
|
||||
return o.repo.DeleteAuthRequest(ctx, id)
|
||||
}
|
||||
|
||||
func (o *OPStorage) CreateToken(ctx context.Context, authReq op.AuthRequest) (string, time.Time, error) {
|
||||
req, err := o.repo.AuthRequestByID(ctx, authReq.GetID())
|
||||
if err != nil {
|
||||
return "", time.Time{}, err
|
||||
}
|
||||
resp, err := o.repo.CreateToken(ctx, req.AgentID, req.ApplicationID, req.UserID, req.Audience, req.Request.(*model.AuthRequestOIDC).Scopes, o.defaultAccessTokenLifetime) //PLANNED: lifetime from client
|
||||
if err != nil {
|
||||
return "", time.Time{}, err
|
||||
}
|
||||
return resp.ID, resp.Expiration, nil
|
||||
}
|
||||
|
||||
func (o *OPStorage) TerminateSession(ctx context.Context, userID, clientID string) error {
|
||||
userAgentID, ok := UserAgentIDFromCtx(ctx)
|
||||
if !ok {
|
||||
return errors.ThrowPreconditionFailed(nil, "OIDC-fso7F", "no user agent id")
|
||||
}
|
||||
return o.repo.SignOut(ctx, userAgentID)
|
||||
}
|
||||
|
||||
func (o *OPStorage) GetSigningKey(ctx context.Context, keyCh chan<- jose.SigningKey, errCh chan<- error, timer <-chan time.Time) {
|
||||
o.repo.GetSigningKey(ctx, keyCh, errCh, timer)
|
||||
}
|
||||
|
||||
func (o *OPStorage) GetKeySet(ctx context.Context) (*jose.JSONWebKeySet, error) {
|
||||
return o.repo.GetKeySet(ctx)
|
||||
}
|
||||
|
||||
func (o *OPStorage) SaveNewKeyPair(ctx context.Context) error {
|
||||
return o.repo.GenerateSigningKeyPair(ctx, o.signingKeyAlgorithm)
|
||||
}
|
253
internal/api/oidc/auth_request_converter.go
Normal file
253
internal/api/oidc/auth_request_converter.go
Normal file
@@ -0,0 +1,253 @@
|
||||
package oidc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/caos/oidc/pkg/oidc"
|
||||
"github.com/caos/oidc/pkg/op"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
http_utils "github.com/caos/zitadel/internal/api/http"
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
amrPassword = "password"
|
||||
amrMFA = "mfa"
|
||||
amrOTP = "otp"
|
||||
)
|
||||
|
||||
type AuthRequest struct {
|
||||
*model.AuthRequest
|
||||
}
|
||||
|
||||
func (a *AuthRequest) GetID() string {
|
||||
return a.ID
|
||||
}
|
||||
|
||||
func (a *AuthRequest) GetACR() string {
|
||||
// return a.
|
||||
return "" //PLANNED: impl
|
||||
}
|
||||
|
||||
func (a *AuthRequest) GetAMR() []string {
|
||||
amr := make([]string, 0)
|
||||
if a.PasswordVerified {
|
||||
amr = append(amr, amrPassword)
|
||||
}
|
||||
if len(a.MfasVerified) > 0 {
|
||||
amr = append(amr, amrMFA)
|
||||
for _, mfa := range a.MfasVerified {
|
||||
if amrMfa := AMRFromMFAType(mfa); amrMfa != "" {
|
||||
amr = append(amr, amrMfa)
|
||||
}
|
||||
}
|
||||
}
|
||||
return amr
|
||||
}
|
||||
|
||||
func (a *AuthRequest) GetAudience() []string {
|
||||
return a.Audience
|
||||
}
|
||||
|
||||
func (a *AuthRequest) GetAuthTime() time.Time {
|
||||
return a.AuthTime
|
||||
}
|
||||
|
||||
func (a *AuthRequest) GetClientID() string {
|
||||
return a.ApplicationID
|
||||
}
|
||||
|
||||
func (a *AuthRequest) GetCodeChallenge() *oidc.CodeChallenge {
|
||||
return CodeChallengeToOIDC(a.oidc().CodeChallenge)
|
||||
}
|
||||
|
||||
func (a *AuthRequest) GetNonce() string {
|
||||
return a.oidc().Nonce
|
||||
}
|
||||
|
||||
func (a *AuthRequest) GetRedirectURI() string {
|
||||
return a.CallbackURI
|
||||
}
|
||||
|
||||
func (a *AuthRequest) GetResponseType() oidc.ResponseType {
|
||||
return ResponseTypeToOIDC(a.oidc().ResponseType)
|
||||
}
|
||||
|
||||
func (a *AuthRequest) GetScopes() []string {
|
||||
return a.oidc().Scopes
|
||||
}
|
||||
|
||||
func (a *AuthRequest) GetState() string {
|
||||
return a.TransferState
|
||||
}
|
||||
|
||||
func (a *AuthRequest) GetSubject() string {
|
||||
return a.UserID
|
||||
}
|
||||
|
||||
func (a *AuthRequest) Done() bool {
|
||||
for _, step := range a.PossibleSteps {
|
||||
if step.Type() == model.NextStepRedirectToCallback {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (a *AuthRequest) oidc() *model.AuthRequestOIDC {
|
||||
return a.Request.(*model.AuthRequestOIDC)
|
||||
}
|
||||
|
||||
func AuthRequestFromBusiness(authReq *model.AuthRequest) (_ op.AuthRequest, err error) {
|
||||
if _, ok := authReq.Request.(*model.AuthRequestOIDC); !ok {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "OIDC-Haz7A", "auth request is not of type oidc")
|
||||
}
|
||||
return &AuthRequest{authReq}, nil
|
||||
}
|
||||
|
||||
func CreateAuthRequestToBusiness(ctx context.Context, authReq *oidc.AuthRequest, userAgentID, userID string) *model.AuthRequest {
|
||||
return &model.AuthRequest{
|
||||
AgentID: userAgentID,
|
||||
BrowserInfo: ParseBrowserInfoFromContext(ctx),
|
||||
ApplicationID: authReq.ClientID,
|
||||
CallbackURI: authReq.RedirectURI,
|
||||
TransferState: authReq.State,
|
||||
Prompt: PromptToBusiness(authReq.Prompt),
|
||||
PossibleLOAs: ACRValuesToBusiness(authReq.ACRValues),
|
||||
UiLocales: UILocalesToBusiness(authReq.UILocales),
|
||||
LoginHint: authReq.LoginHint,
|
||||
MaxAuthAge: authReq.MaxAge,
|
||||
UserID: userID,
|
||||
Request: &model.AuthRequestOIDC{
|
||||
Scopes: authReq.Scopes,
|
||||
ResponseType: ResponseTypeToBusiness(authReq.ResponseType),
|
||||
Nonce: authReq.Nonce,
|
||||
CodeChallenge: CodeChallengeToBusiness(authReq.CodeChallenge, authReq.CodeChallengeMethod),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func ParseBrowserInfoFromContext(ctx context.Context) *model.BrowserInfo {
|
||||
userAgent, acceptLang := HttpHeadersFromContext(ctx)
|
||||
ip := IpFromContext(ctx)
|
||||
return &model.BrowserInfo{RemoteIP: ip, UserAgent: userAgent, AcceptLanguage: acceptLang}
|
||||
}
|
||||
|
||||
func HttpHeadersFromContext(ctx context.Context) (userAgent, acceptLang string) {
|
||||
ctxHeaders, ok := http_utils.HeadersFromCtx(ctx)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if agents, ok := ctxHeaders[http_utils.UserAgentHeader]; ok {
|
||||
userAgent = agents[0]
|
||||
}
|
||||
if langs, ok := ctxHeaders[http_utils.AcceptLanguage]; ok {
|
||||
acceptLang = langs[0]
|
||||
}
|
||||
return userAgent, acceptLang
|
||||
}
|
||||
|
||||
func IpFromContext(ctx context.Context) net.IP {
|
||||
ipString := http_utils.RemoteIPFromCtx(ctx)
|
||||
if ipString == "" {
|
||||
return nil
|
||||
}
|
||||
return net.ParseIP(ipString)
|
||||
}
|
||||
|
||||
func PromptToBusiness(prompt oidc.Prompt) model.Prompt {
|
||||
switch prompt {
|
||||
case oidc.PromptNone:
|
||||
return model.PromptNone
|
||||
case oidc.PromptLogin:
|
||||
return model.PromptLogin
|
||||
case oidc.PromptConsent:
|
||||
return model.PromptConsent
|
||||
case oidc.PromptSelectAccount:
|
||||
return model.PromptSelectAccount
|
||||
default:
|
||||
return model.PromptUnspecified
|
||||
}
|
||||
}
|
||||
|
||||
func ACRValuesToBusiness(values []string) []model.LevelOfAssurance {
|
||||
return nil
|
||||
}
|
||||
|
||||
func UILocalesToBusiness(tags []language.Tag) []string {
|
||||
if tags == nil {
|
||||
return nil
|
||||
}
|
||||
locales := make([]string, len(tags))
|
||||
for i, tag := range tags {
|
||||
locales[i] = tag.String()
|
||||
}
|
||||
return locales
|
||||
}
|
||||
|
||||
func ResponseTypeToBusiness(responseType oidc.ResponseType) model.OIDCResponseType {
|
||||
switch responseType {
|
||||
case oidc.ResponseTypeCode:
|
||||
return model.OIDCResponseTypeCode
|
||||
case oidc.ResponseTypeIDToken:
|
||||
return model.OIDCResponseTypeIdToken
|
||||
case oidc.ResponseTypeIDTokenOnly:
|
||||
return model.OIDCResponseTypeToken
|
||||
default:
|
||||
return model.OIDCResponseTypeCode
|
||||
}
|
||||
}
|
||||
|
||||
func ResponseTypeToOIDC(responseType model.OIDCResponseType) oidc.ResponseType {
|
||||
switch responseType {
|
||||
case model.OIDCResponseTypeCode:
|
||||
return oidc.ResponseTypeCode
|
||||
case model.OIDCResponseTypeToken:
|
||||
return oidc.ResponseTypeIDToken
|
||||
case model.OIDCResponseTypeIdToken:
|
||||
return oidc.ResponseTypeIDTokenOnly
|
||||
default:
|
||||
return oidc.ResponseTypeCode
|
||||
}
|
||||
}
|
||||
|
||||
func CodeChallengeToBusiness(challenge string, method oidc.CodeChallengeMethod) *model.OIDCCodeChallenge {
|
||||
if challenge == "" {
|
||||
return nil
|
||||
}
|
||||
challengeMethod := model.CodeChallengeMethodPlain
|
||||
if method == oidc.CodeChallengeMethodS256 {
|
||||
challengeMethod = model.CodeChallengeMethodS256
|
||||
}
|
||||
return &model.OIDCCodeChallenge{
|
||||
Challenge: challenge,
|
||||
Method: challengeMethod,
|
||||
}
|
||||
}
|
||||
|
||||
func CodeChallengeToOIDC(challenge *model.OIDCCodeChallenge) *oidc.CodeChallenge {
|
||||
if challenge == nil {
|
||||
return nil
|
||||
}
|
||||
challengeMethod := oidc.CodeChallengeMethodPlain
|
||||
if challenge.Method == model.CodeChallengeMethodS256 {
|
||||
challengeMethod = oidc.CodeChallengeMethodS256
|
||||
}
|
||||
return &oidc.CodeChallenge{
|
||||
Challenge: challenge.Challenge,
|
||||
Method: challengeMethod,
|
||||
}
|
||||
}
|
||||
|
||||
func AMRFromMFAType(mfaType model.MfaType) string {
|
||||
switch mfaType {
|
||||
case model.MfaTypeOTP:
|
||||
return amrOTP
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
97
internal/api/oidc/client.go
Normal file
97
internal/api/oidc/client.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package oidc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/oidc/pkg/oidc"
|
||||
"github.com/caos/oidc/pkg/op"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
proj_model "github.com/caos/zitadel/internal/project/model"
|
||||
user_model "github.com/caos/zitadel/internal/user/model"
|
||||
)
|
||||
|
||||
const (
|
||||
scopeOpenID = "openid"
|
||||
scopeProfile = "profile"
|
||||
scopeEmail = "email"
|
||||
scopePhone = "phone"
|
||||
scopeAddress = "address"
|
||||
|
||||
oidcCtx = "oidc"
|
||||
)
|
||||
|
||||
func (o *OPStorage) GetClientByClientID(ctx context.Context, id string) (op.Client, error) {
|
||||
client, err := o.repo.ApplicationByClientID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if client.State != proj_model.AppStateActive {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "OIDC-sdaGg", "client is not active")
|
||||
}
|
||||
return ClientFromBusiness(client, o.defaultLoginURL, o.defaultAccessTokenLifetime, o.defaultIdTokenLifetime)
|
||||
}
|
||||
|
||||
func (o *OPStorage) AuthorizeClientIDSecret(ctx context.Context, id string, secret string) error {
|
||||
ctx = authz.SetCtxData(ctx, authz.CtxData{
|
||||
UserID: oidcCtx,
|
||||
OrgID: oidcCtx,
|
||||
})
|
||||
return o.repo.AuthorizeOIDCApplication(ctx, id, secret)
|
||||
}
|
||||
|
||||
func (o *OPStorage) GetUserinfoFromToken(ctx context.Context, tokenID string) (*oidc.Userinfo, error) {
|
||||
token, err := o.repo.TokenByID(ctx, tokenID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return o.GetUserinfoFromScopes(ctx, token.UserID, token.Scopes)
|
||||
}
|
||||
|
||||
func (o *OPStorage) GetUserinfoFromScopes(ctx context.Context, userID string, scopes []string) (*oidc.Userinfo, error) {
|
||||
user, err := o.repo.UserByID(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userInfo := new(oidc.Userinfo)
|
||||
for _, scope := range scopes {
|
||||
switch scope {
|
||||
case scopeOpenID:
|
||||
userInfo.Subject = user.ID
|
||||
case scopeEmail:
|
||||
userInfo.Email = user.Email
|
||||
userInfo.EmailVerified = user.IsEmailVerified
|
||||
case scopeProfile:
|
||||
userInfo.Name = user.DisplayName
|
||||
userInfo.FamilyName = user.LastName
|
||||
userInfo.GivenName = user.FirstName
|
||||
userInfo.Nickname = user.NickName
|
||||
userInfo.PreferredUsername = user.PreferredLoginName
|
||||
userInfo.UpdatedAt = user.ChangeDate
|
||||
userInfo.Gender = oidc.Gender(getGender(user.Gender))
|
||||
case scopePhone:
|
||||
userInfo.PhoneNumber = user.Phone
|
||||
userInfo.PhoneNumberVerified = user.IsPhoneVerified
|
||||
case scopeAddress:
|
||||
userInfo.Address.StreetAddress = user.StreetAddress
|
||||
userInfo.Address.Locality = user.Locality
|
||||
userInfo.Address.Region = user.Region
|
||||
userInfo.Address.PostalCode = user.PostalCode
|
||||
userInfo.Address.Country = user.Country
|
||||
}
|
||||
}
|
||||
return userInfo, nil
|
||||
}
|
||||
|
||||
func getGender(gender user_model.Gender) string {
|
||||
switch gender {
|
||||
case user_model.GenderFemale:
|
||||
return "female"
|
||||
case user_model.GenderMale:
|
||||
return "male"
|
||||
case user_model.GenderDiverse:
|
||||
return "diverse"
|
||||
}
|
||||
return ""
|
||||
}
|
73
internal/api/oidc/client_converter.go
Normal file
73
internal/api/oidc/client_converter.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package oidc
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/caos/oidc/pkg/op"
|
||||
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/project/model"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
*model.ApplicationView
|
||||
defaultLoginURL string
|
||||
defaultAccessTokenLifetime time.Duration
|
||||
defaultIdTokenLifetime time.Duration
|
||||
}
|
||||
|
||||
func ClientFromBusiness(app *model.ApplicationView, defaultLoginURL string, defaultAccessTokenLifetime, defaultIdTokenLifetime time.Duration) (op.Client, error) {
|
||||
if !app.IsOIDC {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "OIDC-d5bhD", "client is not a proper oidc application")
|
||||
}
|
||||
return &Client{ApplicationView: app, defaultLoginURL: defaultLoginURL, defaultAccessTokenLifetime: defaultAccessTokenLifetime, defaultIdTokenLifetime: defaultIdTokenLifetime}, nil
|
||||
}
|
||||
|
||||
func (c *Client) ApplicationType() op.ApplicationType {
|
||||
return op.ApplicationType(c.OIDCApplicationType)
|
||||
}
|
||||
|
||||
func (c *Client) GetAuthMethod() op.AuthMethod {
|
||||
return authMethodToOIDC(c.OIDCAuthMethodType)
|
||||
}
|
||||
|
||||
func (c *Client) GetID() string {
|
||||
return c.OIDCClientID
|
||||
}
|
||||
|
||||
func (c *Client) LoginURL(id string) string {
|
||||
return c.defaultLoginURL + id
|
||||
}
|
||||
|
||||
func (c *Client) RedirectURIs() []string {
|
||||
return c.OIDCRedirectUris
|
||||
}
|
||||
|
||||
func (c *Client) PostLogoutRedirectURIs() []string {
|
||||
return c.OIDCPostLogoutRedirectUris
|
||||
}
|
||||
|
||||
func (c *Client) AccessTokenLifetime() time.Duration {
|
||||
return c.defaultAccessTokenLifetime //PLANNED: impl from real client
|
||||
}
|
||||
|
||||
func (c *Client) IDTokenLifetime() time.Duration {
|
||||
return c.defaultIdTokenLifetime //PLANNED: impl from real client
|
||||
}
|
||||
|
||||
func (c *Client) AccessTokenType() op.AccessTokenType {
|
||||
return op.AccessTokenTypeBearer //PLANNED: impl from real client
|
||||
}
|
||||
|
||||
func authMethodToOIDC(authType model.OIDCAuthMethodType) op.AuthMethod {
|
||||
switch authType {
|
||||
case model.OIDCAuthMethodTypeBasic:
|
||||
return op.AuthMethodBasic
|
||||
case model.OIDCAuthMethodTypePost:
|
||||
return op.AuthMethodPost
|
||||
case model.OIDCAuthMethodTypeNone:
|
||||
return op.AuthMethodNone
|
||||
default:
|
||||
return op.AuthMethodBasic
|
||||
}
|
||||
}
|
37
internal/api/oidc/cookie_interceptor.go
Normal file
37
internal/api/oidc/cookie_interceptor.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package oidc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
http_utils "github.com/caos/zitadel/internal/api/http"
|
||||
)
|
||||
|
||||
type key int
|
||||
|
||||
var (
|
||||
userAgentKey key
|
||||
)
|
||||
|
||||
func UserAgentIDFromCtx(ctx context.Context) (string, bool) {
|
||||
userAgentID, ok := ctx.Value(userAgentKey).(string)
|
||||
return userAgentID, ok
|
||||
}
|
||||
|
||||
func UserAgentCookieHandler(cookieHandler *http_utils.UserAgentHandler, nextHandlerFunc func(http.HandlerFunc) http.HandlerFunc) func(http.HandlerFunc) http.HandlerFunc {
|
||||
return func(handlerFunc http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ua, err := cookieHandler.GetUserAgent(r)
|
||||
if err != nil {
|
||||
ua, err = cookieHandler.NewUserAgent()
|
||||
}
|
||||
if err == nil {
|
||||
ctx := context.WithValue(r.Context(), userAgentKey, ua.ID)
|
||||
r = r.WithContext(ctx)
|
||||
cookieHandler.SetUserAgent(w, ua)
|
||||
}
|
||||
handlerFunc(w, r)
|
||||
nextHandlerFunc(handlerFunc)
|
||||
}
|
||||
}
|
||||
}
|
95
internal/api/oidc/op.go
Normal file
95
internal/api/oidc/op.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package oidc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/caos/logging"
|
||||
"github.com/caos/oidc/pkg/op"
|
||||
|
||||
http_utils "github.com/caos/zitadel/internal/api/http"
|
||||
"github.com/caos/zitadel/internal/api/http/middleware"
|
||||
"github.com/caos/zitadel/internal/auth/repository"
|
||||
"github.com/caos/zitadel/internal/config/types"
|
||||
"github.com/caos/zitadel/internal/id"
|
||||
)
|
||||
|
||||
type OPHandlerConfig struct {
|
||||
OPConfig *op.Config
|
||||
StorageConfig StorageConfig
|
||||
UserAgentCookieConfig *http_utils.UserAgentCookieConfig
|
||||
Cache *middleware.CacheConfig
|
||||
Endpoints *EndpointConfig
|
||||
}
|
||||
|
||||
type StorageConfig struct {
|
||||
DefaultLoginURL string
|
||||
SigningKeyAlgorithm string
|
||||
DefaultAccessTokenLifetime types.Duration
|
||||
DefaultIdTokenLifetime types.Duration
|
||||
}
|
||||
|
||||
type EndpointConfig struct {
|
||||
Auth *Endpoint
|
||||
Token *Endpoint
|
||||
Userinfo *Endpoint
|
||||
EndSession *Endpoint
|
||||
Keys *Endpoint
|
||||
}
|
||||
|
||||
type Endpoint struct {
|
||||
Path string
|
||||
URL string
|
||||
}
|
||||
|
||||
type OPStorage struct {
|
||||
repo repository.Repository
|
||||
defaultLoginURL string
|
||||
defaultAccessTokenLifetime time.Duration
|
||||
defaultIdTokenLifetime time.Duration
|
||||
signingKeyAlgorithm string
|
||||
}
|
||||
|
||||
func NewProvider(ctx context.Context, config OPHandlerConfig, repo repository.Repository) op.OpenIDProvider {
|
||||
cookieHandler, err := http_utils.NewUserAgentHandler(config.UserAgentCookieConfig, id.SonyFlakeGenerator)
|
||||
logging.Log("OIDC-sd4fd").OnError(err).Panic("cannot user agent handler")
|
||||
nextHandler := func(handlerFunc http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
middleware.NoCacheInterceptor(http_utils.CopyHeadersToContext(handlerFunc))
|
||||
}
|
||||
}
|
||||
provider, err := op.NewDefaultOP(
|
||||
ctx,
|
||||
config.OPConfig,
|
||||
newStorage(config.StorageConfig, repo),
|
||||
op.WithHttpInterceptor(
|
||||
UserAgentCookieHandler(
|
||||
cookieHandler,
|
||||
nextHandler,
|
||||
),
|
||||
),
|
||||
op.WithCustomAuthEndpoint(op.NewEndpointWithURL(config.Endpoints.Auth.Path, config.Endpoints.Auth.URL)),
|
||||
op.WithCustomTokenEndpoint(op.NewEndpointWithURL(config.Endpoints.Token.Path, config.Endpoints.Token.URL)),
|
||||
op.WithCustomUserinfoEndpoint(op.NewEndpointWithURL(config.Endpoints.Userinfo.Path, config.Endpoints.Userinfo.URL)),
|
||||
op.WithCustomEndSessionEndpoint(op.NewEndpointWithURL(config.Endpoints.EndSession.Path, config.Endpoints.EndSession.URL)),
|
||||
op.WithCustomKeysEndpoint(op.NewEndpointWithURL(config.Endpoints.Keys.Path, config.Endpoints.Keys.URL)),
|
||||
op.WithRetry(3, time.Duration(30*time.Second)),
|
||||
)
|
||||
logging.Log("OIDC-asf13").OnError(err).Panic("cannot create provider")
|
||||
return provider
|
||||
}
|
||||
|
||||
func newStorage(config StorageConfig, repo repository.Repository) *OPStorage {
|
||||
return &OPStorage{
|
||||
repo: repo,
|
||||
defaultLoginURL: config.DefaultLoginURL,
|
||||
signingKeyAlgorithm: config.SigningKeyAlgorithm,
|
||||
defaultAccessTokenLifetime: config.DefaultAccessTokenLifetime.Duration,
|
||||
defaultIdTokenLifetime: config.DefaultIdTokenLifetime.Duration,
|
||||
}
|
||||
}
|
||||
|
||||
func (o *OPStorage) Health(ctx context.Context) error {
|
||||
return o.repo.Health(ctx)
|
||||
}
|
Reference in New Issue
Block a user