mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 17:27:31 +00:00
feat: JWT IdP intent (#9966)
# Which Problems Are Solved The login v1 allowed to use JWTs as IdP using the JWT IDP. The login V2 uses idp intents for such cases, which were not yet able to handle JWT IdPs. # How the Problems Are Solved - Added handling of JWT IdPs in `StartIdPIntent` and `RetrieveIdPIntent` - The redirect returned by the start, uses the existing `authRequestID` and `userAgentID` parameter names for compatibility reasons. - Added `/idps/jwt` endpoint to handle the proxied (callback) endpoint , which extracts and validates the JWT against the configured endpoint. # Additional Changes None # Additional Context - closes #9758
This commit is contained in:
@@ -684,6 +684,24 @@ func (i *Instance) AddLDAPProvider(ctx context.Context) string {
|
||||
return resp.GetId()
|
||||
}
|
||||
|
||||
func (i *Instance) AddJWTProvider(ctx context.Context) string {
|
||||
resp, err := i.Client.Admin.AddJWTProvider(ctx, &admin.AddJWTProviderRequest{
|
||||
Name: "jwt-idp",
|
||||
Issuer: "https://example.com",
|
||||
JwtEndpoint: "https://example.com/jwt",
|
||||
KeysEndpoint: "https://example.com/keys",
|
||||
HeaderName: "Authorization",
|
||||
ProviderOptions: &idp.Options{
|
||||
IsLinkingAllowed: true,
|
||||
IsCreationAllowed: true,
|
||||
IsAutoCreation: true,
|
||||
IsAutoUpdate: true,
|
||||
},
|
||||
})
|
||||
logging.OnError(err).Panic("create jwt idp")
|
||||
return resp.GetId()
|
||||
}
|
||||
|
||||
func (i *Instance) CreateIntent(ctx context.Context, idpID string) *user_v2.StartIdentityProviderIntentResponse {
|
||||
resp, err := i.Client.UserV2.StartIdentityProviderIntent(ctx, &user_v2.StartIdentityProviderIntentRequest{
|
||||
IdpId: idpID,
|
||||
|
@@ -27,6 +27,7 @@ import (
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/idp/providers/jwt"
|
||||
"github.com/zitadel/zitadel/internal/idp/providers/ldap"
|
||||
"github.com/zitadel/zitadel/internal/idp/providers/oauth"
|
||||
openid "github.com/zitadel/zitadel/internal/idp/providers/oidc"
|
||||
@@ -124,6 +125,25 @@ func SuccessfulLDAPIntent(instanceID, idpID, idpUserID, userID string) (string,
|
||||
return resp.IntentID, resp.Token, resp.ChangeDate, resp.Sequence, nil
|
||||
}
|
||||
|
||||
func SuccessfulJWTIntent(instanceID, idpID, idpUserID, userID string, expiry time.Time) (string, string, time.Time, uint64, error) {
|
||||
u := url.URL{
|
||||
Scheme: "http",
|
||||
Host: host,
|
||||
Path: successfulIntentJWTPath(),
|
||||
}
|
||||
resp, err := callIntent(u.String(), &SuccessfulIntentRequest{
|
||||
InstanceID: instanceID,
|
||||
IDPID: idpID,
|
||||
IDPUserID: idpUserID,
|
||||
UserID: userID,
|
||||
Expiry: expiry,
|
||||
})
|
||||
if err != nil {
|
||||
return "", "", time.Time{}, uint64(0), err
|
||||
}
|
||||
return resp.IntentID, resp.Token, resp.ChangeDate, resp.Sequence, nil
|
||||
}
|
||||
|
||||
// StartServer starts a simple HTTP server on localhost:8081
|
||||
// ZITADEL can use the server to send HTTP requests which can be
|
||||
// used to validate tests through [Subscribe]rs.
|
||||
@@ -145,6 +165,7 @@ func StartServer(commands *command.Commands) (close func()) {
|
||||
router.HandleFunc(successfulIntentOIDCPath(), successfulIntentHandler(commands, createSuccessfulOIDCIntent))
|
||||
router.HandleFunc(successfulIntentSAMLPath(), successfulIntentHandler(commands, createSuccessfulSAMLIntent))
|
||||
router.HandleFunc(successfulIntentLDAPPath(), successfulIntentHandler(commands, createSuccessfulLDAPIntent))
|
||||
router.HandleFunc(successfulIntentJWTPath(), successfulIntentHandler(commands, createSuccessfulJWTIntent))
|
||||
}
|
||||
s := &http.Server{
|
||||
Addr: listenAddr,
|
||||
@@ -195,6 +216,10 @@ func successfulIntentLDAPPath() string {
|
||||
return path.Join(successfulIntentPath(), "/", "ldap")
|
||||
}
|
||||
|
||||
func successfulIntentJWTPath() string {
|
||||
return path.Join(successfulIntentPath(), "/", "jwt")
|
||||
}
|
||||
|
||||
// forwarder handles incoming HTTP requests from ZITADEL and
|
||||
// forwards them to all subscribed web sockets.
|
||||
type forwarder struct {
|
||||
@@ -497,3 +522,30 @@ func createSuccessfulLDAPIntent(ctx context.Context, cmd *command.Commands, req
|
||||
writeModel.ProcessedSequence,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func createSuccessfulJWTIntent(ctx context.Context, cmd *command.Commands, req *SuccessfulIntentRequest) (*SuccessfulIntentResponse, error) {
|
||||
intentID, err := createIntent(ctx, cmd, req.InstanceID, req.IDPID)
|
||||
writeModel, err := cmd.GetIntentWriteModel(ctx, intentID, req.InstanceID)
|
||||
idpUser := &jwt.User{
|
||||
IDTokenClaims: &oidc.IDTokenClaims{
|
||||
TokenClaims: oidc.TokenClaims{
|
||||
Subject: req.IDPUserID,
|
||||
},
|
||||
},
|
||||
}
|
||||
session := &jwt.Session{
|
||||
Tokens: &oidc.Tokens[*oidc.IDTokenClaims]{
|
||||
IDToken: "idToken",
|
||||
},
|
||||
}
|
||||
token, err := cmd.SucceedIDPIntent(ctx, writeModel, idpUser, session, req.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &SuccessfulIntentResponse{
|
||||
intentID,
|
||||
token,
|
||||
writeModel.ChangeDate,
|
||||
writeModel.ProcessedSequence,
|
||||
}, nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user