feat: refresh token (#1728)

* begin refresh tokens

* refresh tokens

* list and revoke refresh tokens

* handle remove

* tests for refresh tokens

* uniqueness and default expiration

* rename oidc token methods

* cleanup

* migration version

* Update internal/static/i18n/en.yaml

Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com>

* fixes

* feat: update oidc pkg for refresh tokens

Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com>
This commit is contained in:
Livio Amstutz
2021-05-20 13:33:35 +02:00
committed by GitHub
parent bc21eeb114
commit ec5020bebc
36 changed files with 2732 additions and 55 deletions

View File

@@ -80,7 +80,7 @@ func (o *OPStorage) DeleteAuthRequest(ctx context.Context, id string) (err error
return o.repo.DeleteAuthRequest(ctx, id)
}
func (o *OPStorage) CreateToken(ctx context.Context, req op.TokenRequest) (_ string, _ time.Time, err error) {
func (o *OPStorage) CreateAccessToken(ctx context.Context, req op.TokenRequest) (_ string, _ time.Time, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
var userAgentID, applicationID, userOrgID string
@@ -107,6 +107,37 @@ func grantsToScopes(grants []*grant_model.UserGrantView) []string {
return scopes
}
func (o *OPStorage) CreateAccessAndRefreshTokens(ctx context.Context, req op.TokenRequest, refreshToken string) (_, _ string, _ time.Time, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
var userAgentID, applicationID, userOrgID string
var authTime time.Time
var authMethodsReferences []string
authReq, ok := req.(*AuthRequest)
if ok {
userAgentID = authReq.AgentID
applicationID = authReq.ApplicationID
userOrgID = authReq.UserOrgID
authTime = authReq.AuthTime
authMethodsReferences = authReq.GetAMR()
}
resp, token, err := o.command.AddAccessAndRefreshToken(ctx, userOrgID, userAgentID, applicationID, req.GetSubject(),
refreshToken, req.GetAudience(), req.GetScopes(), authMethodsReferences, o.defaultAccessTokenLifetime,
o.defaultRefreshTokenIdleExpiration, o.defaultRefreshTokenExpiration, authTime) //PLANNED: lifetime from client
if err != nil {
return "", "", time.Time{}, err
}
return resp.TokenID, token, resp.Expiration, nil
}
func (o *OPStorage) TokenRequestByRefreshToken(ctx context.Context, refreshToken string) (op.RefreshTokenRequest, error) {
tokenView, err := o.repo.RefreshTokenByID(ctx, refreshToken)
if err != nil {
return nil, err
}
return RefreshTokenRequestFromBusiness(tokenView), nil
}
func (o *OPStorage) TerminateSession(ctx context.Context, userID, clientID string) (err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()

View File

@@ -2,7 +2,6 @@ package oidc
import (
"context"
"github.com/caos/zitadel/internal/domain"
"net"
"time"
@@ -11,7 +10,9 @@ import (
"golang.org/x/text/language"
http_utils "github.com/caos/zitadel/internal/api/http"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/user/model"
)
const (
@@ -255,3 +256,39 @@ func AMRFromMFAType(mfaType domain.MFAType) string {
return ""
}
}
func RefreshTokenRequestFromBusiness(tokenView *model.RefreshTokenView) op.RefreshTokenRequest {
return &RefreshTokenRequest{tokenView}
}
type RefreshTokenRequest struct {
*model.RefreshTokenView
}
func (r *RefreshTokenRequest) GetAMR() []string {
return r.AuthMethodsReferences
}
func (r *RefreshTokenRequest) GetAudience() []string {
return r.Audience
}
func (r *RefreshTokenRequest) GetAuthTime() time.Time {
return r.AuthTime
}
func (r *RefreshTokenRequest) GetClientID() string {
return r.ClientID
}
func (r *RefreshTokenRequest) GetScopes() []string {
return r.Scopes
}
func (r *RefreshTokenRequest) GetSubject() string {
return r.UserID
}
func (r *RefreshTokenRequest) SetCurrentScopes(scopes oidc.Scopes) {
r.Scopes = scopes
}

View File

@@ -28,10 +28,12 @@ type OPHandlerConfig struct {
}
type StorageConfig struct {
DefaultLoginURL string
SigningKeyAlgorithm string
DefaultAccessTokenLifetime types.Duration
DefaultIdTokenLifetime types.Duration
DefaultLoginURL string
SigningKeyAlgorithm string
DefaultAccessTokenLifetime types.Duration
DefaultIdTokenLifetime types.Duration
DefaultRefreshTokenIdleExpiration types.Duration
DefaultRefreshTokenExpiration types.Duration
}
type EndpointConfig struct {
@@ -49,13 +51,15 @@ type Endpoint struct {
}
type OPStorage struct {
repo repository.Repository
command *command.Commands
query *query.Queries
defaultLoginURL string
defaultAccessTokenLifetime time.Duration
defaultIdTokenLifetime time.Duration
signingKeyAlgorithm string
repo repository.Repository
command *command.Commands
query *query.Queries
defaultLoginURL string
defaultAccessTokenLifetime time.Duration
defaultIdTokenLifetime time.Duration
signingKeyAlgorithm string
defaultRefreshTokenIdleExpiration time.Duration
defaultRefreshTokenExpiration time.Duration
}
func NewProvider(ctx context.Context, config OPHandlerConfig, command *command.Commands, query *query.Queries, repo repository.Repository, keyConfig *crypto.KeyConfig, localDevMode bool) op.OpenIDProvider {
@@ -94,13 +98,15 @@ func NewProvider(ctx context.Context, config OPHandlerConfig, command *command.C
func newStorage(config StorageConfig, command *command.Commands, query *query.Queries, repo repository.Repository) *OPStorage {
return &OPStorage{
repo: repo,
command: command,
query: query,
defaultLoginURL: config.DefaultLoginURL,
signingKeyAlgorithm: config.SigningKeyAlgorithm,
defaultAccessTokenLifetime: config.DefaultAccessTokenLifetime.Duration,
defaultIdTokenLifetime: config.DefaultIdTokenLifetime.Duration,
repo: repo,
command: command,
query: query,
defaultLoginURL: config.DefaultLoginURL,
signingKeyAlgorithm: config.SigningKeyAlgorithm,
defaultAccessTokenLifetime: config.DefaultAccessTokenLifetime.Duration,
defaultIdTokenLifetime: config.DefaultIdTokenLifetime.Duration,
defaultRefreshTokenIdleExpiration: config.DefaultRefreshTokenIdleExpiration.Duration,
defaultRefreshTokenExpiration: config.DefaultRefreshTokenExpiration.Duration,
}
}