mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 17:27:31 +00:00
feat: actions context information add clientID (#10339)
# Which Problems Are Solved There is no information contained in the context info sent to Actions v2. # How the Problems Are Solved Add application information to the context information sent to Actions v2, to give more information about the execution. # Additional Changes None # Additional Context Closes #9377
This commit is contained in:
@@ -605,7 +605,7 @@ func TestServer_ExecutionTargetPreUserinfo(t *testing.T) {
|
|||||||
{Key: "added", Value: "value"},
|
{Key: "added", Value: "value"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return expectPreUserinfoExecution(ctx, t, instance, req, response)
|
return expectPreUserinfoExecution(ctx, t, instance, client.GetClientId(), req, response)
|
||||||
},
|
},
|
||||||
req: &oidc_pb.CreateCallbackRequest{
|
req: &oidc_pb.CreateCallbackRequest{
|
||||||
AuthRequestId: func() string {
|
AuthRequestId: func() string {
|
||||||
@@ -630,7 +630,7 @@ func TestServer_ExecutionTargetPreUserinfo(t *testing.T) {
|
|||||||
"addedLog",
|
"addedLog",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return expectPreUserinfoExecution(ctx, t, instance, req, response)
|
return expectPreUserinfoExecution(ctx, t, instance, client.GetClientId(), req, response)
|
||||||
},
|
},
|
||||||
req: &oidc_pb.CreateCallbackRequest{
|
req: &oidc_pb.CreateCallbackRequest{
|
||||||
AuthRequestId: func() string {
|
AuthRequestId: func() string {
|
||||||
@@ -655,7 +655,7 @@ func TestServer_ExecutionTargetPreUserinfo(t *testing.T) {
|
|||||||
{Key: "key", Value: []byte("value")},
|
{Key: "key", Value: []byte("value")},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return expectPreUserinfoExecution(ctx, t, instance, req, response)
|
return expectPreUserinfoExecution(ctx, t, instance, client.GetClientId(), req, response)
|
||||||
},
|
},
|
||||||
req: &oidc_pb.CreateCallbackRequest{
|
req: &oidc_pb.CreateCallbackRequest{
|
||||||
AuthRequestId: func() string {
|
AuthRequestId: func() string {
|
||||||
@@ -692,7 +692,7 @@ func TestServer_ExecutionTargetPreUserinfo(t *testing.T) {
|
|||||||
{Key: "added3", Value: "value3"},
|
{Key: "added3", Value: "value3"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return expectPreUserinfoExecution(ctx, t, instance, req, response)
|
return expectPreUserinfoExecution(ctx, t, instance, client.GetClientId(), req, response)
|
||||||
},
|
},
|
||||||
req: &oidc_pb.CreateCallbackRequest{
|
req: &oidc_pb.CreateCallbackRequest{
|
||||||
AuthRequestId: func() string {
|
AuthRequestId: func() string {
|
||||||
@@ -755,7 +755,7 @@ func TestServer_ExecutionTargetPreUserinfo(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func expectPreUserinfoExecution(ctx context.Context, t *testing.T, instance *integration.Instance, req *oidc_pb.CreateCallbackRequest, response *oidc_api.ContextInfoResponse) (string, func()) {
|
func expectPreUserinfoExecution(ctx context.Context, t *testing.T, instance *integration.Instance, clientID string, req *oidc_pb.CreateCallbackRequest, response *oidc_api.ContextInfoResponse) (string, func()) {
|
||||||
userEmail := gofakeit.Email()
|
userEmail := gofakeit.Email()
|
||||||
userPhone := "+41" + gofakeit.Phone()
|
userPhone := "+41" + gofakeit.Phone()
|
||||||
userResp := instance.CreateHumanUserVerified(ctx, instance.DefaultOrg.Id, userEmail, userPhone)
|
userResp := instance.CreateHumanUserVerified(ctx, instance.DefaultOrg.Id, userEmail, userPhone)
|
||||||
@@ -767,7 +767,7 @@ func expectPreUserinfoExecution(ctx context.Context, t *testing.T, instance *int
|
|||||||
SessionToken: sessionResp.GetSessionToken(),
|
SessionToken: sessionResp.GetSessionToken(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
expectedContextInfo := contextInfoForUserOIDC(instance, "function/preuserinfo", userResp, userEmail, userPhone)
|
expectedContextInfo := contextInfoForUserOIDC(instance, "function/preuserinfo", clientID, userResp, userEmail, userPhone)
|
||||||
|
|
||||||
targetURL, closeF, _, _ := integration.TestServerCall(expectedContextInfo, 0, http.StatusOK, response)
|
targetURL, closeF, _, _ := integration.TestServerCall(expectedContextInfo, 0, http.StatusOK, response)
|
||||||
|
|
||||||
@@ -845,7 +845,7 @@ func getAccessTokenClaims(ctx context.Context, t *testing.T, instance *integrati
|
|||||||
return claims
|
return claims
|
||||||
}
|
}
|
||||||
|
|
||||||
func contextInfoForUserOIDC(instance *integration.Instance, function string, userResp *user.AddHumanUserResponse, email, phone string) *oidc_api.ContextInfo {
|
func contextInfoForUserOIDC(instance *integration.Instance, function string, clientID string, userResp *user.AddHumanUserResponse, email, phone string) *oidc_api.ContextInfo {
|
||||||
return &oidc_api.ContextInfo{
|
return &oidc_api.ContextInfo{
|
||||||
Function: function,
|
Function: function,
|
||||||
UserInfo: &oidc.UserInfo{
|
UserInfo: &oidc.UserInfo{
|
||||||
@@ -878,6 +878,9 @@ func contextInfoForUserOIDC(instance *integration.Instance, function string, use
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
UserMetadata: nil,
|
UserMetadata: nil,
|
||||||
|
Application: &oidc_api.ContextInfoApplication{
|
||||||
|
ClientID: clientID,
|
||||||
|
},
|
||||||
Org: &query.UserInfoOrg{
|
Org: &query.UserInfoOrg{
|
||||||
ID: instance.DefaultOrg.GetId(),
|
ID: instance.DefaultOrg.GetId(),
|
||||||
Name: instance.DefaultOrg.GetName(),
|
Name: instance.DefaultOrg.GetName(),
|
||||||
@@ -918,7 +921,7 @@ func TestServer_ExecutionTargetPreAccessToken(t *testing.T) {
|
|||||||
{Key: "added1", Value: "value"},
|
{Key: "added1", Value: "value"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return expectPreAccessTokenExecution(ctx, t, instance, req, response)
|
return expectPreAccessTokenExecution(ctx, t, instance, client.GetClientId(), req, response)
|
||||||
},
|
},
|
||||||
req: &oidc_pb.CreateCallbackRequest{
|
req: &oidc_pb.CreateCallbackRequest{
|
||||||
AuthRequestId: func() string {
|
AuthRequestId: func() string {
|
||||||
@@ -943,7 +946,7 @@ func TestServer_ExecutionTargetPreAccessToken(t *testing.T) {
|
|||||||
"addedLog",
|
"addedLog",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return expectPreAccessTokenExecution(ctx, t, instance, req, response)
|
return expectPreAccessTokenExecution(ctx, t, instance, client.GetClientId(), req, response)
|
||||||
},
|
},
|
||||||
req: &oidc_pb.CreateCallbackRequest{
|
req: &oidc_pb.CreateCallbackRequest{
|
||||||
AuthRequestId: func() string {
|
AuthRequestId: func() string {
|
||||||
@@ -968,7 +971,7 @@ func TestServer_ExecutionTargetPreAccessToken(t *testing.T) {
|
|||||||
{Key: "key", Value: []byte("value")},
|
{Key: "key", Value: []byte("value")},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return expectPreAccessTokenExecution(ctx, t, instance, req, response)
|
return expectPreAccessTokenExecution(ctx, t, instance, client.GetClientId(), req, response)
|
||||||
},
|
},
|
||||||
req: &oidc_pb.CreateCallbackRequest{
|
req: &oidc_pb.CreateCallbackRequest{
|
||||||
AuthRequestId: func() string {
|
AuthRequestId: func() string {
|
||||||
@@ -1005,7 +1008,7 @@ func TestServer_ExecutionTargetPreAccessToken(t *testing.T) {
|
|||||||
{Key: "added3", Value: "value3"},
|
{Key: "added3", Value: "value3"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return expectPreAccessTokenExecution(ctx, t, instance, req, response)
|
return expectPreAccessTokenExecution(ctx, t, instance, client.GetClientId(), req, response)
|
||||||
},
|
},
|
||||||
req: &oidc_pb.CreateCallbackRequest{
|
req: &oidc_pb.CreateCallbackRequest{
|
||||||
AuthRequestId: func() string {
|
AuthRequestId: func() string {
|
||||||
@@ -1060,7 +1063,7 @@ func TestServer_ExecutionTargetPreAccessToken(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func expectPreAccessTokenExecution(ctx context.Context, t *testing.T, instance *integration.Instance, req *oidc_pb.CreateCallbackRequest, response *oidc_api.ContextInfoResponse) (string, func()) {
|
func expectPreAccessTokenExecution(ctx context.Context, t *testing.T, instance *integration.Instance, clientID string, req *oidc_pb.CreateCallbackRequest, response *oidc_api.ContextInfoResponse) (string, func()) {
|
||||||
userEmail := gofakeit.Email()
|
userEmail := gofakeit.Email()
|
||||||
userPhone := "+41" + gofakeit.Phone()
|
userPhone := "+41" + gofakeit.Phone()
|
||||||
userResp := instance.CreateHumanUserVerified(ctx, instance.DefaultOrg.Id, userEmail, userPhone)
|
userResp := instance.CreateHumanUserVerified(ctx, instance.DefaultOrg.Id, userEmail, userPhone)
|
||||||
@@ -1072,7 +1075,7 @@ func expectPreAccessTokenExecution(ctx context.Context, t *testing.T, instance *
|
|||||||
SessionToken: sessionResp.GetSessionToken(),
|
SessionToken: sessionResp.GetSessionToken(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
expectedContextInfo := contextInfoForUserOIDC(instance, "function/preaccesstoken", userResp, userEmail, userPhone)
|
expectedContextInfo := contextInfoForUserOIDC(instance, "function/preaccesstoken", clientID, userResp, userEmail, userPhone)
|
||||||
|
|
||||||
targetURL, closeF, _, _ := integration.TestServerCall(expectedContextInfo, 0, http.StatusOK, response)
|
targetURL, closeF, _, _ := integration.TestServerCall(expectedContextInfo, 0, http.StatusOK, response)
|
||||||
|
|
||||||
|
@@ -100,6 +100,7 @@ func (s *Server) Introspect(ctx context.Context, r *op.Request[op.IntrospectionR
|
|||||||
token.userID,
|
token.userID,
|
||||||
token.scope,
|
token.scope,
|
||||||
client.projectID,
|
client.projectID,
|
||||||
|
client.clientID,
|
||||||
client.projectRoleAssertion,
|
client.projectRoleAssertion,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
@@ -31,7 +31,7 @@ for example the v2 code exchange and refresh token.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
func (s *Server) accessTokenResponseFromSession(ctx context.Context, client op.Client, session *command.OIDCSession, state, projectID string, projectRoleAssertion, accessTokenRoleAssertion, idTokenRoleAssertion, userInfoAssertion bool) (_ *oidc.AccessTokenResponse, err error) {
|
func (s *Server) accessTokenResponseFromSession(ctx context.Context, client op.Client, session *command.OIDCSession, state, projectID string, projectRoleAssertion, accessTokenRoleAssertion, idTokenRoleAssertion, userInfoAssertion bool) (_ *oidc.AccessTokenResponse, err error) {
|
||||||
getUserInfo := s.getUserInfo(session.UserID, projectID, projectRoleAssertion, userInfoAssertion, session.Scope)
|
getUserInfo := s.getUserInfo(session.UserID, projectID, client.GetID(), projectRoleAssertion, userInfoAssertion, session.Scope)
|
||||||
getSigner := s.getSignerOnce()
|
getSigner := s.getSignerOnce()
|
||||||
|
|
||||||
resp := &oidc.AccessTokenResponse{
|
resp := &oidc.AccessTokenResponse{
|
||||||
@@ -113,8 +113,8 @@ type userInfoFunc func(ctx context.Context, roleAssertion bool, triggerType doma
|
|||||||
|
|
||||||
// getUserInfo returns a function which retrieves userinfo from the database once.
|
// getUserInfo returns a function which retrieves userinfo from the database once.
|
||||||
// However, each time, role claims are asserted and also action flows will trigger.
|
// However, each time, role claims are asserted and also action flows will trigger.
|
||||||
func (s *Server) getUserInfo(userID, projectID string, projectRoleAssertion, userInfoAssertion bool, scope []string) userInfoFunc {
|
func (s *Server) getUserInfo(userID, projectID, clientID string, projectRoleAssertion, userInfoAssertion bool, scope []string) userInfoFunc {
|
||||||
userInfo := s.userInfo(userID, scope, projectID, projectRoleAssertion, userInfoAssertion, false)
|
userInfo := s.userInfo(userID, scope, projectID, clientID, projectRoleAssertion, userInfoAssertion, false)
|
||||||
return func(ctx context.Context, roleAssertion bool, triggerType domain.TriggerType) (*oidc.UserInfo, error) {
|
return func(ctx context.Context, roleAssertion bool, triggerType domain.TriggerType) (*oidc.UserInfo, error) {
|
||||||
return userInfo(ctx, roleAssertion, triggerType)
|
return userInfo(ctx, roleAssertion, triggerType)
|
||||||
}
|
}
|
||||||
|
@@ -218,7 +218,7 @@ func validateTokenExchangeAudience(requestedAudience, subjectAudience, actorAudi
|
|||||||
// Both tokens may point to the same object (subjectToken) in case of a regular Token Exchange.
|
// Both tokens may point to the same object (subjectToken) in case of a regular Token Exchange.
|
||||||
// When the subject and actor Tokens point to different objects, the new tokens will be for impersonation / delegation.
|
// When the subject and actor Tokens point to different objects, the new tokens will be for impersonation / delegation.
|
||||||
func (s *Server) createExchangeTokens(ctx context.Context, tokenType oidc.TokenType, client *Client, subjectToken, actorToken *exchangeToken, audience, scopes []string) (_ *oidc.TokenExchangeResponse, err error) {
|
func (s *Server) createExchangeTokens(ctx context.Context, tokenType oidc.TokenType, client *Client, subjectToken, actorToken *exchangeToken, audience, scopes []string) (_ *oidc.TokenExchangeResponse, err error) {
|
||||||
getUserInfo := s.getUserInfo(subjectToken.userID, client.client.ProjectID, client.client.ProjectRoleAssertion, client.IDTokenUserinfoClaimsAssertion(), scopes)
|
getUserInfo := s.getUserInfo(subjectToken.userID, client.client.ProjectID, client.GetID(), client.client.ProjectRoleAssertion, client.IDTokenUserinfoClaimsAssertion(), scopes)
|
||||||
getSigner := s.getSignerOnce()
|
getSigner := s.getSignerOnce()
|
||||||
|
|
||||||
resp := &oidc.TokenExchangeResponse{
|
resp := &oidc.TokenExchangeResponse{
|
||||||
|
@@ -54,6 +54,7 @@ func (s *Server) UserInfo(ctx context.Context, r *op.Request[oidc.UserInfoReques
|
|||||||
token.userID,
|
token.userID,
|
||||||
token.scope,
|
token.scope,
|
||||||
projectID,
|
projectID,
|
||||||
|
token.clientID,
|
||||||
assertion,
|
assertion,
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
@@ -86,6 +87,7 @@ func (s *Server) userInfo(
|
|||||||
userID string,
|
userID string,
|
||||||
scope []string,
|
scope []string,
|
||||||
projectID string,
|
projectID string,
|
||||||
|
clientID string,
|
||||||
projectRoleAssertion, userInfoAssertion, currentProjectOnly bool,
|
projectRoleAssertion, userInfoAssertion, currentProjectOnly bool,
|
||||||
) func(ctx context.Context, roleAssertion bool, triggerType domain.TriggerType) (_ *oidc.UserInfo, err error) {
|
) func(ctx context.Context, roleAssertion bool, triggerType domain.TriggerType) (_ *oidc.UserInfo, err error) {
|
||||||
var (
|
var (
|
||||||
@@ -120,7 +122,7 @@ func (s *Server) userInfo(
|
|||||||
Claims: maps.Clone(rawUserInfo.Claims),
|
Claims: maps.Clone(rawUserInfo.Claims),
|
||||||
}
|
}
|
||||||
assertRoles(projectID, qu, roleAudience, requestedRoles, roleAssertion, userInfo)
|
assertRoles(projectID, qu, roleAudience, requestedRoles, roleAssertion, userInfo)
|
||||||
return userInfo, s.userinfoFlows(ctx, qu, userInfo, triggerType)
|
return userInfo, s.userinfoFlows(ctx, qu, userInfo, triggerType, clientID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,7 +287,8 @@ func setUserInfoRoleClaims(userInfo *oidc.UserInfo, roles *projectsRoles) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) userinfoFlows(ctx context.Context, qu *query.OIDCUserInfo, userInfo *oidc.UserInfo, triggerType domain.TriggerType) (err error) {
|
//nolint:gocognit
|
||||||
|
func (s *Server) userinfoFlows(ctx context.Context, qu *query.OIDCUserInfo, userInfo *oidc.UserInfo, triggerType domain.TriggerType, clientID string) (err error) {
|
||||||
ctx, span := tracing.NewSpan(ctx)
|
ctx, span := tracing.NewSpan(ctx)
|
||||||
defer func() { span.EndWithError(err) }()
|
defer func() { span.EndWithError(err) }()
|
||||||
|
|
||||||
@@ -319,6 +322,13 @@ func (s *Server) userinfoFlows(ctx context.Context, qu *query.OIDCUserInfo, user
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
actions.SetFields("application",
|
||||||
|
actions.SetFields("getClientId", func(c *actions.FieldConfig) interface{} {
|
||||||
|
return func(goja.FunctionCall) goja.Value {
|
||||||
|
return c.Runtime.ToValue(clientID)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -427,6 +437,7 @@ func (s *Server) userinfoFlows(ctx context.Context, qu *query.OIDCUserInfo, user
|
|||||||
User: qu.User,
|
User: qu.User,
|
||||||
UserMetadata: qu.Metadata,
|
UserMetadata: qu.Metadata,
|
||||||
Org: qu.Org,
|
Org: qu.Org,
|
||||||
|
Application: &ContextInfoApplication{ClientID: clientID},
|
||||||
UserGrants: qu.UserGrants,
|
UserGrants: qu.UserGrants,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -463,13 +474,17 @@ func (s *Server) userinfoFlows(ctx context.Context, qu *query.OIDCUserInfo, user
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ContextInfo struct {
|
type ContextInfo struct {
|
||||||
Function string `json:"function,omitempty"`
|
Function string `json:"function,omitempty"`
|
||||||
UserInfo *oidc.UserInfo `json:"userinfo,omitempty"`
|
UserInfo *oidc.UserInfo `json:"userinfo,omitempty"`
|
||||||
User *query.User `json:"user,omitempty"`
|
User *query.User `json:"user,omitempty"`
|
||||||
UserMetadata []query.UserMetadata `json:"user_metadata,omitempty"`
|
UserMetadata []query.UserMetadata `json:"user_metadata,omitempty"`
|
||||||
Org *query.UserInfoOrg `json:"org,omitempty"`
|
Org *query.UserInfoOrg `json:"org,omitempty"`
|
||||||
UserGrants []query.UserGrant `json:"user_grants,omitempty"`
|
UserGrants []query.UserGrant `json:"user_grants,omitempty"`
|
||||||
Response *ContextInfoResponse `json:"response,omitempty"`
|
Application *ContextInfoApplication `json:"application,omitempty"`
|
||||||
|
Response *ContextInfoResponse `json:"response,omitempty"`
|
||||||
|
}
|
||||||
|
type ContextInfoApplication struct {
|
||||||
|
ClientID string `json:"client_id,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ContextInfoResponse struct {
|
type ContextInfoResponse struct {
|
||||||
|
Reference in New Issue
Block a user