diff --git a/pkg/client/client.go b/pkg/client/client.go index 9eda973..f6a407b 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -61,12 +61,18 @@ func callTokenEndpoint(request interface{}, authFn interface{}, caller TokenEndp if err := httphelper.HttpRequest(caller.HttpClient(), req, &tokenRes); err != nil { return nil, err } - return &oauth2.Token{ + token := &oauth2.Token{ AccessToken: tokenRes.AccessToken, TokenType: tokenRes.TokenType, RefreshToken: tokenRes.RefreshToken, Expiry: time.Now().UTC().Add(time.Duration(tokenRes.ExpiresIn) * time.Second), - }, nil + } + if tokenRes.IDToken != "" { + token = token.WithExtra(map[string]any{ + "id_token": tokenRes.IDToken, + }) + } + return token, nil } type EndSessionCaller interface { diff --git a/pkg/client/integration_test.go b/pkg/client/integration_test.go index e19a720..40e1bee 100644 --- a/pkg/client/integration_test.go +++ b/pkg/client/integration_test.go @@ -53,6 +53,7 @@ func TestRelyingPartySession(t *testing.T) { t.Logf("new token type %s", newTokens.TokenType) t.Logf("new expiry %s", newTokens.Expiry.Format(time.RFC3339)) require.NotEmpty(t, newTokens.AccessToken, "new accessToken") + assert.NotEmpty(t, newTokens.Extra("id_token"), "new idToken") t.Log("------ end session (logout) ------") @@ -141,7 +142,6 @@ func TestResourceServerTokenExchange(t *testing.T) { require.Error(t, err, "refresh token") assert.Contains(t, err.Error(), "subject_token is invalid") require.Nil(t, tokenExchangeResponse, "token exchange response") - } func RunAuthorizationCodeFlow(t *testing.T, opServer *httptest.Server, clientID, clientSecret string) (provider rp.RelyingParty, accessToken, refreshToken, idToken string) { diff --git a/pkg/client/profile/jwt_profile.go b/pkg/client/profile/jwt_profile.go index a934f7d..a220dc5 100644 --- a/pkg/client/profile/jwt_profile.go +++ b/pkg/client/profile/jwt_profile.go @@ -13,7 +13,7 @@ import ( // jwtProfileTokenSource implement the oauth2.TokenSource // it will request a token using the OAuth2 JWT Profile Grant -// therefore sending an `assertion` by singing a JWT with the provided private key +// therefore sending an `assertion` by signing a JWT with the provided private key type jwtProfileTokenSource struct { clientID string audience []string diff --git a/pkg/client/rp/relying_party.go b/pkg/client/rp/relying_party.go index ede7453..7127020 100644 --- a/pkg/client/rp/relying_party.go +++ b/pkg/client/rp/relying_party.go @@ -620,6 +620,10 @@ type RefreshTokenRequest struct { GrantType oidc.GrantType `schema:"grant_type"` } +// RefreshAccessToken performs a token refresh. If it doesn't error, it will always +// provide a new AccessToken. It may provide a new RefreshToken, and if it does, then +// the old one should be considered invalid. It may also provide a new IDToken. The +// new IDToken can be retrieved with token.Extra("id_token"). func RefreshAccessToken(rp RelyingParty, refreshToken, clientAssertion, clientAssertionType string) (*oauth2.Token, error) { request := RefreshTokenRequest{ RefreshToken: refreshToken,