fix: set refresh token in the RetrieveIdentityIntentResponse (#11613)

# Which Problems Are Solved

This PR adds support for storing and retrieving refresh tokens from
external identity providers during the IDP intent flow.

# How the Problems Are Solved

* Updated `idp.proto` (v2 and v2beta): added an optional `refresh_token`
field to `IDPOAuthAccessInformation`, which, in turn, is used in
`RetrieveIdentityProviderIntentResponse`
* Added the `IDPRefreshToken` field to the IDP intent `SucceededEvent`
struct, and updated the corresponding constructor to set the refresh
token
* Added the `IDPRefreshToken` field to `IDPIntentWriteModel`, and
updated `reduceOAuthSucceededEvent` to populate refresh token from
events
* Updated `tokensForSucceededIDPIntent` function to extract and encrypt
the refresh token from IDP session, if set
* Updated `idpOAuthTokensToPb` function to decrypt refresh token before
returning to clients
* Updated unit and integration tests

# Additional Changes
Updated the link to the JWT IDP docs linked in the Console

# Additional Context
- Closes https://github.com/zitadel/zitadel/issues/11047
This commit is contained in:
Gayathri Vijayan
2026-02-19 06:06:30 +00:00
committed by GitHub
parent a5a5b65931
commit 6ad1ef859b
13 changed files with 224 additions and 77 deletions
@@ -425,8 +425,9 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) {
IdpInformation: &user.IDPInformation{
Access: &user.IDPInformation_Oauth{
Oauth: &user.IDPOAuthAccessInformation{
AccessToken: "accessToken",
IdToken: gu.Ptr("idToken"),
AccessToken: "accessToken",
RefreshToken: gu.Ptr("refreshToken"),
IdToken: gu.Ptr("idToken"),
},
},
IdpId: oauthIdpID,
@@ -476,8 +477,9 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) {
IdpInformation: &user.IDPInformation{
Access: &user.IDPInformation_Oauth{
Oauth: &user.IDPOAuthAccessInformation{
AccessToken: "accessToken",
IdToken: gu.Ptr("idToken"),
AccessToken: "accessToken",
RefreshToken: gu.Ptr("refreshToken"),
IdToken: gu.Ptr("idToken"),
},
},
IdpId: oauthIdpID,
@@ -543,8 +545,9 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) {
IdpInformation: &user.IDPInformation{
Access: &user.IDPInformation_Oauth{
Oauth: &user.IDPOAuthAccessInformation{
AccessToken: "accessToken",
IdToken: gu.Ptr("idToken"),
AccessToken: "accessToken",
RefreshToken: gu.Ptr("refreshToken"),
IdToken: gu.Ptr("idToken"),
},
},
IdpId: azureIdpID,
@@ -601,8 +604,9 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) {
IdpInformation: &user.IDPInformation{
Access: &user.IDPInformation_Oauth{
Oauth: &user.IDPOAuthAccessInformation{
AccessToken: "accessToken",
IdToken: gu.Ptr("idToken"),
AccessToken: "accessToken",
RefreshToken: gu.Ptr("refreshToken"),
IdToken: gu.Ptr("idToken"),
},
},
IdpId: azureIdpID,
@@ -657,8 +661,9 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) {
IdpInformation: &user.IDPInformation{
Access: &user.IDPInformation_Oauth{
Oauth: &user.IDPOAuthAccessInformation{
AccessToken: "accessToken",
IdToken: gu.Ptr("idToken"),
AccessToken: "accessToken",
RefreshToken: gu.Ptr("refreshToken"),
IdToken: gu.Ptr("idToken"),
},
},
IdpId: oidcIdpID,
@@ -707,8 +712,9 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) {
IdpInformation: &user.IDPInformation{
Access: &user.IDPInformation_Oauth{
Oauth: &user.IDPOAuthAccessInformation{
AccessToken: "accessToken",
IdToken: gu.Ptr("idToken"),
AccessToken: "accessToken",
RefreshToken: gu.Ptr("refreshToken"),
IdToken: gu.Ptr("idToken"),
},
},
IdpId: oidcIdpID,
+13 -4
View File
@@ -250,7 +250,7 @@ func idpIntentToIDPIntentPb(intent *command.IDPIntentWriteModel, alg crypto.Encr
information.Details = intentToDetailsPb(intent)
// OAuth / OIDC
if intent.IDPIDToken != "" || intent.IDPAccessToken != nil {
information.IdpInformation.Access, err = idpOAuthTokensToPb(intent.IDPIDToken, intent.IDPAccessToken, alg)
information.IdpInformation.Access, err = idpOAuthTokensToPb(intent.IDPIDToken, intent.IDPAccessToken, intent.IDPRefreshToken, alg)
if err != nil {
return nil, err
}
@@ -274,7 +274,7 @@ func idpIntentToIDPIntentPb(intent *command.IDPIntentWriteModel, alg crypto.Encr
return information, nil
}
func idpOAuthTokensToPb(idpIDToken string, idpAccessToken *crypto.CryptoValue, alg crypto.EncryptionAlgorithm) (_ *user.IDPInformation_Oauth, err error) {
func idpOAuthTokensToPb(idpIDToken string, idpAccessToken, idpRefreshToken *crypto.CryptoValue, alg crypto.EncryptionAlgorithm) (_ *user.IDPInformation_Oauth, err error) {
var idToken *string
if idpIDToken != "" {
idToken = &idpIDToken
@@ -286,10 +286,19 @@ func idpOAuthTokensToPb(idpIDToken string, idpAccessToken *crypto.CryptoValue, a
return nil, err
}
}
var refreshToken *string
if idpRefreshToken != nil {
decryptedRefreshToken, err := crypto.DecryptString(idpRefreshToken, alg)
if err != nil {
return nil, err
}
refreshToken = &decryptedRefreshToken
}
return &user.IDPInformation_Oauth{
Oauth: &user.IDPOAuthAccessInformation{
AccessToken: accessToken,
IdToken: idToken,
AccessToken: accessToken,
RefreshToken: refreshToken,
IdToken: idToken,
},
}, nil
}
@@ -2299,8 +2299,9 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) {
IdpInformation: &user.IDPInformation{
Access: &user.IDPInformation_Oauth{
Oauth: &user.IDPOAuthAccessInformation{
AccessToken: "accessToken",
IdToken: gu.Ptr("idToken"),
AccessToken: "accessToken",
RefreshToken: gu.Ptr("refreshToken"),
IdToken: gu.Ptr("idToken"),
},
},
IdpId: oauthIdpID,
@@ -2339,8 +2340,9 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) {
IdpInformation: &user.IDPInformation{
Access: &user.IDPInformation_Oauth{
Oauth: &user.IDPOAuthAccessInformation{
AccessToken: "accessToken",
IdToken: gu.Ptr("idToken"),
AccessToken: "accessToken",
RefreshToken: gu.Ptr("refreshToken"),
IdToken: gu.Ptr("idToken"),
},
},
IdpId: oauthIdpID,
@@ -2401,8 +2403,9 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) {
IdpInformation: &user.IDPInformation{
Access: &user.IDPInformation_Oauth{
Oauth: &user.IDPOAuthAccessInformation{
AccessToken: "accessToken",
IdToken: gu.Ptr("idToken"),
AccessToken: "accessToken",
RefreshToken: gu.Ptr("refreshToken"),
IdToken: gu.Ptr("idToken"),
},
},
IdpId: oidcIdpID,
@@ -2439,8 +2442,9 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) {
IdpInformation: &user.IDPInformation{
Access: &user.IDPInformation_Oauth{
Oauth: &user.IDPOAuthAccessInformation{
AccessToken: "accessToken",
IdToken: gu.Ptr("idToken"),
AccessToken: "accessToken",
RefreshToken: gu.Ptr("refreshToken"),
IdToken: gu.Ptr("idToken"),
},
},
IdpId: oidcIdpID,
+13 -4
View File
@@ -522,7 +522,7 @@ func idpIntentToIDPIntentPb(intent *command.IDPIntentWriteModel, alg crypto.Encr
UserId: intent.UserID,
}
if intent.IDPIDToken != "" || intent.IDPAccessToken != nil {
information.IdpInformation.Access, err = idpOAuthTokensToPb(intent.IDPIDToken, intent.IDPAccessToken, alg)
information.IdpInformation.Access, err = idpOAuthTokensToPb(intent.IDPIDToken, intent.IDPAccessToken, intent.IDPRefreshToken, alg)
if err != nil {
return nil, err
}
@@ -547,7 +547,7 @@ func idpIntentToIDPIntentPb(intent *command.IDPIntentWriteModel, alg crypto.Encr
return connect.NewResponse(information), nil
}
func idpOAuthTokensToPb(idpIDToken string, idpAccessToken *crypto.CryptoValue, alg crypto.EncryptionAlgorithm) (_ *user.IDPInformation_Oauth, err error) {
func idpOAuthTokensToPb(idpIDToken string, idpAccessToken, idpRefreshToken *crypto.CryptoValue, alg crypto.EncryptionAlgorithm) (_ *user.IDPInformation_Oauth, err error) {
var idToken *string
if idpIDToken != "" {
idToken = &idpIDToken
@@ -559,10 +559,19 @@ func idpOAuthTokensToPb(idpIDToken string, idpAccessToken *crypto.CryptoValue, a
return nil, err
}
}
var refreshToken *string
if idpRefreshToken != nil {
decryptedRefreshToken, err := crypto.DecryptString(idpRefreshToken, alg)
if err != nil {
return nil, err
}
refreshToken = &decryptedRefreshToken
}
return &user.IDPInformation_Oauth{
Oauth: &user.IDPOAuthAccessInformation{
AccessToken: accessToken,
IdToken: idToken,
AccessToken: accessToken,
RefreshToken: refreshToken,
IdToken: idToken,
},
}, nil
}