feat: add http request to interal and external authentication actions (#5103)

Add functionality to provide http.Request and authError to actions for logging or other logic.
This commit is contained in:
Stefan Benz
2023-01-26 11:40:49 +01:00
committed by GitHub
parent fd4f1dd016
commit eb17d0c378
14 changed files with 170 additions and 29 deletions

View File

@@ -3,6 +3,7 @@ package login
import (
"context"
"encoding/json"
"net/http"
"github.com/dop251/goja"
"github.com/zitadel/oidc/v2/pkg/oidc"
@@ -15,8 +16,17 @@ import (
iam_model "github.com/zitadel/zitadel/internal/iam/model"
)
func (l *Login) customExternalUserMapping(ctx context.Context, user *domain.ExternalUser, tokens *oidc.Tokens, req *domain.AuthRequest, config *iam_model.IDPConfigView) (*domain.ExternalUser, error) {
resourceOwner := req.RequestedOrgID
func (l *Login) runPostExternalAuthenticationActions(
user *domain.ExternalUser,
tokens *oidc.Tokens,
authRequest *domain.AuthRequest,
httpRequest *http.Request,
config *iam_model.IDPConfigView,
authenticationError error,
) (*domain.ExternalUser, error) {
ctx := httpRequest.Context()
resourceOwner := authRequest.RequestedOrgID
if resourceOwner == "" {
resourceOwner = config.AggregateID
}
@@ -69,6 +79,11 @@ func (l *Login) customExternalUserMapping(ctx context.Context, user *domain.Exte
),
)
authErrStr := "none"
if authenticationError != nil {
authErrStr = authenticationError.Error()
}
for _, a := range triggerActions {
actionCtx, cancel := context.WithTimeout(ctx, a.Timeout())
@@ -77,7 +92,9 @@ func (l *Login) customExternalUserMapping(ctx context.Context, user *domain.Exte
actions.SetFields("externalUser", func(c *actions.FieldConfig) interface{} {
return object.UserFromExternalUser(c, user)
}),
actions.SetFields("authRequest", object.AuthRequestField(req)),
actions.SetFields("authRequest", object.AuthRequestField(authRequest)),
actions.SetFields("httpRequest", object.HTTPRequestField(httpRequest)),
actions.SetFields("authError", authErrStr),
),
)
@@ -109,10 +126,17 @@ const (
authMethodPasswordless authMethod = "passwordless"
)
func (l *Login) triggerPostLocalAuthentication(ctx context.Context, req *domain.AuthRequest, authMethod authMethod, authenticationError error) ([]*domain.Metadata, error) {
resourceOwner := req.RequestedOrgID
func (l *Login) runPostInternalAuthenticationActions(
authRequest *domain.AuthRequest,
httpRequest *http.Request,
authMethod authMethod,
authenticationError error,
) ([]*domain.Metadata, error) {
ctx := httpRequest.Context()
resourceOwner := authRequest.RequestedOrgID
if resourceOwner == "" {
resourceOwner = req.UserOrgID
resourceOwner = authRequest.UserOrgID
}
triggerActions, err := l.query.GetActiveActionsByFlowAndTriggerType(ctx, domain.FlowTypeInternalAuthentication, domain.TriggerTypePostAuthentication, resourceOwner, false)
@@ -136,12 +160,12 @@ func (l *Login) triggerPostLocalAuthentication(ctx context.Context, req *domain.
if authenticationError != nil {
authErrStr = authenticationError.Error()
}
ctxFields := actions.SetContextFields(
actions.SetFields("v1",
actions.SetFields("authMethod", authMethod),
actions.SetFields("authError", authErrStr),
actions.SetFields("authRequest", object.AuthRequestField(req)),
actions.SetFields("authRequest", object.AuthRequestField(authRequest)),
actions.SetFields("httpRequest", object.HTTPRequestField(httpRequest)),
),
)
@@ -161,7 +185,16 @@ func (l *Login) triggerPostLocalAuthentication(ctx context.Context, req *domain.
return object.MetadataListToDomain(metadataList), err
}
func (l *Login) customUserToLoginUserMapping(ctx context.Context, authRequest *domain.AuthRequest, user *domain.Human, metadata []*domain.Metadata, resourceOwner string, flowType domain.FlowType) (*domain.Human, []*domain.Metadata, error) {
func (l *Login) runPreCreationActions(
authRequest *domain.AuthRequest,
httpRequest *http.Request,
user *domain.Human,
metadata []*domain.Metadata,
resourceOwner string,
flowType domain.FlowType,
) (*domain.Human, []*domain.Metadata, error) {
ctx := httpRequest.Context()
triggerActions, err := l.query.GetActiveActionsByFlowAndTriggerType(ctx, flowType, domain.TriggerTypePreCreation, resourceOwner, false)
if err != nil {
return nil, nil, err
@@ -231,6 +264,7 @@ func (l *Login) customUserToLoginUserMapping(ctx context.Context, authRequest *d
return object.UserFromHuman(c, user)
}),
actions.SetFields("authRequest", object.AuthRequestField(authRequest)),
actions.SetFields("httpRequest", object.HTTPRequestField(httpRequest)),
),
)
@@ -250,7 +284,15 @@ func (l *Login) customUserToLoginUserMapping(ctx context.Context, authRequest *d
return user, object.MetadataListToDomain(metadataList), err
}
func (l *Login) customGrants(ctx context.Context, userID string, authRequest *domain.AuthRequest, resourceOwner string, flowType domain.FlowType) ([]*domain.UserGrant, error) {
func (l *Login) runPostCreationActions(
userID string,
authRequest *domain.AuthRequest,
httpRequest *http.Request,
resourceOwner string,
flowType domain.FlowType,
) ([]*domain.UserGrant, error) {
ctx := httpRequest.Context()
triggerActions, err := l.query.GetActiveActionsByFlowAndTriggerType(ctx, flowType, domain.TriggerTypePostCreation, resourceOwner, false)
if err != nil {
return nil, err
@@ -280,6 +322,7 @@ func (l *Login) customGrants(ctx context.Context, userID string, authRequest *do
}
}),
actions.SetFields("authRequest", object.AuthRequestField(authRequest)),
actions.SetFields("httpRequest", object.HTTPRequestField(httpRequest)),
),
)

View File

@@ -8,6 +8,7 @@ import (
"strings"
"time"
"github.com/zitadel/logging"
"github.com/zitadel/oidc/v2/pkg/client/rp"
"github.com/zitadel/oidc/v2/pkg/oidc"
"golang.org/x/oauth2"
@@ -155,18 +156,35 @@ func (l *Login) handleExternalLoginCallback(w http.ResponseWriter, r *http.Reque
if idpConfig.IsOIDC {
provider, err := l.getRPConfig(r.Context(), idpConfig, EndpointExternalLoginCallback)
if err != nil {
emtpyTokens := &oidc.Tokens{Token: &oauth2.Token{}}
if _, actionErr := l.runPostExternalAuthenticationActions(&domain.ExternalUser{}, emtpyTokens, authReq, r, idpConfig, err); actionErr != nil {
logging.WithError(err).Error("both external user authentication and action post authentication failed")
}
l.renderLogin(w, r, authReq, err)
return
}
tokens, err := rp.CodeExchange(r.Context(), data.Code, provider)
if err != nil {
emtpyTokens := &oidc.Tokens{Token: &oauth2.Token{}}
if _, actionErr := l.runPostExternalAuthenticationActions(&domain.ExternalUser{}, emtpyTokens, authReq, r, idpConfig, err); actionErr != nil {
logging.WithError(err).Error("both external user authentication and action post authentication failed")
}
l.renderLogin(w, r, authReq, err)
return
}
l.handleExternalUserAuthenticated(w, r, authReq, idpConfig, userAgentID, tokens)
return
}
l.renderError(w, r, authReq, errors.ThrowPreconditionFailed(nil, "RP-asff2", "Errors.ExternalIDP.IDPTypeNotImplemented"))
err = errors.ThrowPreconditionFailed(nil, "RP-asff2", "Errors.ExternalIDP.IDPTypeNotImplemented")
emtpyTokens := &oidc.Tokens{Token: &oauth2.Token{}}
if _, actionErr := l.runPostExternalAuthenticationActions(&domain.ExternalUser{}, emtpyTokens, authReq, r, idpConfig, err); actionErr != nil {
logging.WithError(err).Error("both external user authentication and action post authentication failed")
}
l.renderError(w, r, authReq, err)
}
func (l *Login) getRPConfig(ctx context.Context, idpConfig *iam_model.IDPConfigView, callbackEndpoint string) (rp.RelyingParty, error) {
@@ -195,7 +213,7 @@ func (l *Login) getRPConfig(ctx context.Context, idpConfig *iam_model.IDPConfigV
func (l *Login) handleExternalUserAuthenticated(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, idpConfig *iam_model.IDPConfigView, userAgentID string, tokens *oidc.Tokens) {
externalUser := l.mapTokenToLoginUser(tokens, idpConfig)
externalUser, err := l.customExternalUserMapping(r.Context(), externalUser, tokens, authReq, idpConfig)
externalUser, err := l.runPostExternalAuthenticationActions(externalUser, tokens, authReq, r, idpConfig, nil)
if err != nil {
l.renderError(w, r, authReq, err)
return
@@ -383,7 +401,7 @@ func (l *Login) handleAutoRegister(w http.ResponseWriter, r *http.Request, authR
user, externalIDP, metadata := l.mapExternalUserToLoginUser(orgIamPolicy, linkingUser, idpConfig)
user, metadata, err = l.customUserToLoginUserMapping(r.Context(), authReq, user, metadata, resourceOwner, domain.FlowTypeExternalAuthentication)
user, metadata, err = l.runPreCreationActions(authReq, r, user, metadata, resourceOwner, domain.FlowTypeExternalAuthentication)
if err != nil {
l.renderExternalNotFoundOption(w, r, authReq, orgIamPolicy, nil, nil, err)
return
@@ -398,7 +416,7 @@ func (l *Login) handleAutoRegister(w http.ResponseWriter, r *http.Request, authR
l.renderError(w, r, authReq, err)
return
}
userGrants, err := l.customGrants(r.Context(), authReq.UserID, authReq, resourceOwner, domain.FlowTypeExternalAuthentication)
userGrants, err := l.runPostCreationActions(authReq.UserID, authReq, r, resourceOwner, domain.FlowTypeExternalAuthentication)
if err != nil {
l.renderError(w, r, authReq, err)
return

View File

@@ -121,7 +121,7 @@ func (l *Login) handleExternalUserRegister(w http.ResponseWriter, r *http.Reques
resourceOwner = authReq.RequestedOrgID
}
externalUser, externalIDP := l.mapTokenToLoginHumanAndExternalIDP(tokens, idpConfig)
externalUser, err := l.customExternalUserMapping(r.Context(), externalUser, tokens, authReq, idpConfig)
externalUser, err := l.runPostExternalAuthenticationActions(externalUser, tokens, authReq, r, idpConfig, nil)
if err != nil {
l.renderRegisterOption(w, r, authReq, err)
return
@@ -161,7 +161,7 @@ func (l *Login) registerExternalUser(w http.ResponseWriter, r *http.Request, aut
return
}
user, externalIDP, metadata := l.mapExternalUserToLoginUser(orgIamPolicy, externalUser, idpConfig)
user, metadata, err = l.customUserToLoginUserMapping(r.Context(), authReq, user, metadata, resourceOwner, domain.FlowTypeExternalAuthentication)
user, metadata, err = l.runPreCreationActions(authReq, r, user, metadata, resourceOwner, domain.FlowTypeExternalAuthentication)
if err != nil {
l.renderRegisterOption(w, r, authReq, err)
return
@@ -177,7 +177,7 @@ func (l *Login) registerExternalUser(w http.ResponseWriter, r *http.Request, aut
l.renderError(w, r, authReq, err)
return
}
userGrants, err := l.customGrants(r.Context(), authReq.UserID, authReq, resourceOwner, domain.FlowTypeExternalAuthentication)
userGrants, err := l.runPostCreationActions(authReq.UserID, authReq, r, resourceOwner, domain.FlowTypeExternalAuthentication)
if err != nil {
l.renderError(w, r, authReq, err)
return

View File

@@ -67,17 +67,25 @@ func (l *Login) handleJWTRequest(w http.ResponseWriter, r *http.Request) {
func (l *Login) handleJWTExtraction(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, idpConfig *iam_model.IDPConfigView) {
token, err := getToken(r, idpConfig.JWTHeaderName)
if err != nil {
emtpyTokens := &oidc.Tokens{Token: &oauth2.Token{}}
if _, actionErr := l.runPostExternalAuthenticationActions(&domain.ExternalUser{}, emtpyTokens, authReq, r, idpConfig, err); actionErr != nil {
logging.WithError(err).Error("both external user authentication and action post authentication failed")
}
l.renderError(w, r, authReq, err)
return
}
tokenClaims, err := validateToken(r.Context(), token, idpConfig)
tokens := &oidc.Tokens{IDToken: token, IDTokenClaims: tokenClaims, Token: &oauth2.Token{}}
if err != nil {
if _, actionErr := l.runPostExternalAuthenticationActions(&domain.ExternalUser{}, tokens, authReq, r, idpConfig, err); actionErr != nil {
logging.WithError(err).Error("both external user authentication and action post authentication failed")
}
l.renderError(w, r, authReq, err)
return
}
tokens := &oidc.Tokens{IDToken: token, IDTokenClaims: tokenClaims, Token: &oauth2.Token{}}
externalUser := l.mapTokenToLoginUser(tokens, idpConfig)
externalUser, err = l.customExternalUserMapping(r.Context(), externalUser, tokens, authReq, idpConfig)
externalUser, err = l.runPostExternalAuthenticationActions(externalUser, tokens, authReq, r, idpConfig, nil)
if err != nil {
l.renderError(w, r, authReq, err)
return
@@ -129,7 +137,7 @@ func (l *Login) jwtExtractionUserNotFound(w http.ResponseWriter, r *http.Request
}
user, externalIDP, metadata := l.mapExternalUserToLoginUser(orgIamPolicy, authReq.LinkingUsers[len(authReq.LinkingUsers)-1], idpConfig)
user, metadata, err = l.customUserToLoginUserMapping(r.Context(), authReq, user, metadata, resourceOwner, domain.FlowTypeExternalAuthentication)
user, metadata, err = l.runPreCreationActions(authReq, r, user, metadata, resourceOwner, domain.FlowTypeExternalAuthentication)
if err != nil {
l.renderError(w, r, authReq, err)
return
@@ -144,7 +152,7 @@ func (l *Login) jwtExtractionUserNotFound(w http.ResponseWriter, r *http.Request
l.renderError(w, r, authReq, err)
return
}
userGrants, err := l.customGrants(r.Context(), authReq.UserID, authReq, resourceOwner, domain.FlowTypeExternalAuthentication)
userGrants, err := l.runPostCreationActions(authReq.UserID, authReq, r, resourceOwner, domain.FlowTypeExternalAuthentication)
if err != nil {
l.renderError(w, r, authReq, err)
return

View File

@@ -37,7 +37,7 @@ func (l *Login) handleMFAVerify(w http.ResponseWriter, r *http.Request) {
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
err = l.authRepo.VerifyMFAOTP(setContext(r.Context(), authReq.UserOrgID), authReq.ID, authReq.UserID, authReq.UserOrgID, data.Code, userAgentID, domain.BrowserInfoFromRequest(r))
metadata, actionErr := l.triggerPostLocalAuthentication(r.Context(), authReq, authMethodOTP, err)
metadata, actionErr := l.runPostInternalAuthenticationActions(authReq, r, authMethodOTP, err)
if err == nil && actionErr == nil && len(metadata) > 0 {
_, err = l.command.BulkSetUserMetadata(r.Context(), authReq.UserID, authReq.UserOrgID, metadata...)
} else if actionErr != nil && err == nil {

View File

@@ -72,7 +72,7 @@ func (l *Login) handleU2FVerification(w http.ResponseWriter, r *http.Request) {
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
err = l.authRepo.VerifyMFAU2F(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, authReq.UserOrgID, authReq.ID, userAgentID, credData, domain.BrowserInfoFromRequest(r))
metadata, actionErr := l.triggerPostLocalAuthentication(r.Context(), authReq, authMethodU2F, err)
metadata, actionErr := l.runPostInternalAuthenticationActions(authReq, r, authMethodU2F, err)
if err == nil && actionErr == nil && len(metadata) > 0 {
_, err = l.command.BulkSetUserMetadata(r.Context(), authReq.UserID, authReq.UserOrgID, metadata...)
} else if actionErr != nil && err == nil {

View File

@@ -40,7 +40,7 @@ func (l *Login) handlePasswordCheck(w http.ResponseWriter, r *http.Request) {
}
err = l.authRepo.VerifyPassword(setContext(r.Context(), authReq.UserOrgID), authReq.ID, authReq.UserID, authReq.UserOrgID, data.Password, authReq.AgentID, domain.BrowserInfoFromRequest(r))
metadata, actionErr := l.triggerPostLocalAuthentication(r.Context(), authReq, authMethodPassword, err)
metadata, actionErr := l.runPostInternalAuthenticationActions(authReq, r, authMethodPassword, err)
if err == nil && actionErr == nil && len(metadata) > 0 {
_, err = l.command.BulkSetUserMetadata(r.Context(), authReq.UserID, authReq.UserOrgID, metadata...)
} else if actionErr != nil && err == nil {

View File

@@ -64,7 +64,7 @@ func (l *Login) handlePasswordlessVerification(w http.ResponseWriter, r *http.Re
}
err = l.authRepo.VerifyPasswordless(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, authReq.UserOrgID, authReq.ID, authReq.AgentID, credData, domain.BrowserInfoFromRequest(r))
metadata, actionErr := l.triggerPostLocalAuthentication(r.Context(), authReq, authMethodPasswordless, err)
metadata, actionErr := l.runPostInternalAuthenticationActions(authReq, r, authMethodPasswordless, err)
if err == nil && actionErr == nil && len(metadata) > 0 {
_, err = l.command.BulkSetUserMetadata(r.Context(), authReq.UserID, authReq.UserOrgID, metadata...)
} else if actionErr != nil && err == nil {

View File

@@ -91,7 +91,7 @@ func (l *Login) handleRegisterCheck(w http.ResponseWriter, r *http.Request) {
// without breaking existing actions.
// Also, if that field is needed, we probably also should provide it
// for ExternalAuthentication.
user, metadatas, err := l.customUserToLoginUserMapping(r.Context(), authRequest, data.toHumanDomain(), make([]*domain.Metadata, 0), resourceOwner, domain.FlowTypeInternalAuthentication)
user, metadatas, err := l.runPreCreationActions(authRequest, r, data.toHumanDomain(), make([]*domain.Metadata, 0), resourceOwner, domain.FlowTypeInternalAuthentication)
if err != nil {
l.renderRegister(w, r, authRequest, data, err)
return
@@ -112,7 +112,7 @@ func (l *Login) handleRegisterCheck(w http.ResponseWriter, r *http.Request) {
}
}
userGrants, err := l.customGrants(r.Context(), user.AggregateID, authRequest, resourceOwner, domain.FlowTypeInternalAuthentication)
userGrants, err := l.runPostCreationActions(user.AggregateID, authRequest, r, resourceOwner, domain.FlowTypeInternalAuthentication)
if err != nil {
l.renderError(w, r, authRequest, err)
return