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:
Stefan Benz
2025-07-29 00:08:12 +02:00
committed by GitHub
parent 5d2d1d6da6
commit 416a35537f
5 changed files with 45 additions and 26 deletions

View File

@@ -605,7 +605,7 @@ func TestServer_ExecutionTargetPreUserinfo(t *testing.T) {
{Key: "added", Value: "value"},
},
}
return expectPreUserinfoExecution(ctx, t, instance, req, response)
return expectPreUserinfoExecution(ctx, t, instance, client.GetClientId(), req, response)
},
req: &oidc_pb.CreateCallbackRequest{
AuthRequestId: func() string {
@@ -630,7 +630,7 @@ func TestServer_ExecutionTargetPreUserinfo(t *testing.T) {
"addedLog",
},
}
return expectPreUserinfoExecution(ctx, t, instance, req, response)
return expectPreUserinfoExecution(ctx, t, instance, client.GetClientId(), req, response)
},
req: &oidc_pb.CreateCallbackRequest{
AuthRequestId: func() string {
@@ -655,7 +655,7 @@ func TestServer_ExecutionTargetPreUserinfo(t *testing.T) {
{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{
AuthRequestId: func() string {
@@ -692,7 +692,7 @@ func TestServer_ExecutionTargetPreUserinfo(t *testing.T) {
{Key: "added3", Value: "value3"},
},
}
return expectPreUserinfoExecution(ctx, t, instance, req, response)
return expectPreUserinfoExecution(ctx, t, instance, client.GetClientId(), req, response)
},
req: &oidc_pb.CreateCallbackRequest{
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()
userPhone := "+41" + gofakeit.Phone()
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(),
},
}
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)
@@ -845,7 +845,7 @@ func getAccessTokenClaims(ctx context.Context, t *testing.T, instance *integrati
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{
Function: function,
UserInfo: &oidc.UserInfo{
@@ -878,6 +878,9 @@ func contextInfoForUserOIDC(instance *integration.Instance, function string, use
},
},
UserMetadata: nil,
Application: &oidc_api.ContextInfoApplication{
ClientID: clientID,
},
Org: &query.UserInfoOrg{
ID: instance.DefaultOrg.GetId(),
Name: instance.DefaultOrg.GetName(),
@@ -918,7 +921,7 @@ func TestServer_ExecutionTargetPreAccessToken(t *testing.T) {
{Key: "added1", Value: "value"},
},
}
return expectPreAccessTokenExecution(ctx, t, instance, req, response)
return expectPreAccessTokenExecution(ctx, t, instance, client.GetClientId(), req, response)
},
req: &oidc_pb.CreateCallbackRequest{
AuthRequestId: func() string {
@@ -943,7 +946,7 @@ func TestServer_ExecutionTargetPreAccessToken(t *testing.T) {
"addedLog",
},
}
return expectPreAccessTokenExecution(ctx, t, instance, req, response)
return expectPreAccessTokenExecution(ctx, t, instance, client.GetClientId(), req, response)
},
req: &oidc_pb.CreateCallbackRequest{
AuthRequestId: func() string {
@@ -968,7 +971,7 @@ func TestServer_ExecutionTargetPreAccessToken(t *testing.T) {
{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{
AuthRequestId: func() string {
@@ -1005,7 +1008,7 @@ func TestServer_ExecutionTargetPreAccessToken(t *testing.T) {
{Key: "added3", Value: "value3"},
},
}
return expectPreAccessTokenExecution(ctx, t, instance, req, response)
return expectPreAccessTokenExecution(ctx, t, instance, client.GetClientId(), req, response)
},
req: &oidc_pb.CreateCallbackRequest{
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()
userPhone := "+41" + gofakeit.Phone()
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(),
},
}
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)

View File

@@ -100,6 +100,7 @@ func (s *Server) Introspect(ctx context.Context, r *op.Request[op.IntrospectionR
token.userID,
token.scope,
client.projectID,
client.clientID,
client.projectRoleAssertion,
true,
true,

View File

@@ -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) {
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()
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.
// 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 {
userInfo := s.userInfo(userID, scope, projectID, projectRoleAssertion, userInfoAssertion, false)
func (s *Server) getUserInfo(userID, projectID, clientID string, projectRoleAssertion, userInfoAssertion bool, scope []string) userInfoFunc {
userInfo := s.userInfo(userID, scope, projectID, clientID, projectRoleAssertion, userInfoAssertion, false)
return func(ctx context.Context, roleAssertion bool, triggerType domain.TriggerType) (*oidc.UserInfo, error) {
return userInfo(ctx, roleAssertion, triggerType)
}

View File

@@ -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.
// 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) {
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()
resp := &oidc.TokenExchangeResponse{

View File

@@ -54,6 +54,7 @@ func (s *Server) UserInfo(ctx context.Context, r *op.Request[oidc.UserInfoReques
token.userID,
token.scope,
projectID,
token.clientID,
assertion,
true,
false,
@@ -86,6 +87,7 @@ func (s *Server) userInfo(
userID string,
scope []string,
projectID string,
clientID string,
projectRoleAssertion, userInfoAssertion, currentProjectOnly bool,
) func(ctx context.Context, roleAssertion bool, triggerType domain.TriggerType) (_ *oidc.UserInfo, err error) {
var (
@@ -120,7 +122,7 @@ func (s *Server) userInfo(
Claims: maps.Clone(rawUserInfo.Claims),
}
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)
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,
UserMetadata: qu.Metadata,
Org: qu.Org,
Application: &ContextInfoApplication{ClientID: clientID},
UserGrants: qu.UserGrants,
}
@@ -463,13 +474,17 @@ func (s *Server) userinfoFlows(ctx context.Context, qu *query.OIDCUserInfo, user
}
type ContextInfo struct {
Function string `json:"function,omitempty"`
UserInfo *oidc.UserInfo `json:"userinfo,omitempty"`
User *query.User `json:"user,omitempty"`
UserMetadata []query.UserMetadata `json:"user_metadata,omitempty"`
Org *query.UserInfoOrg `json:"org,omitempty"`
UserGrants []query.UserGrant `json:"user_grants,omitempty"`
Response *ContextInfoResponse `json:"response,omitempty"`
Function string `json:"function,omitempty"`
UserInfo *oidc.UserInfo `json:"userinfo,omitempty"`
User *query.User `json:"user,omitempty"`
UserMetadata []query.UserMetadata `json:"user_metadata,omitempty"`
Org *query.UserInfoOrg `json:"org,omitempty"`
UserGrants []query.UserGrant `json:"user_grants,omitempty"`
Application *ContextInfoApplication `json:"application,omitempty"`
Response *ContextInfoResponse `json:"response,omitempty"`
}
type ContextInfoApplication struct {
ClientID string `json:"client_id,omitempty"`
}
type ContextInfoResponse struct {