Enhance refresh token functionality and response handling
- Updated MakeRefreshToken and generateRefreshToken methods to accept an optional expiresIn parameter for customizable token expiration. - Modified storeRefreshTokenWithScope to handle dynamic expiration times based on the provided parameter. - Enhanced the authback function to send cookies with access and refresh tokens, including their respective expiration times. - Updated LoginResponse structure to include RefreshTokenExpiresIn for better client-side management of token lifetimes. - Adjusted client configuration to set default refresh token expiration duration.
This commit is contained in:
parent
c1b28f3b69
commit
5657c75201
5 changed files with 45 additions and 25 deletions
|
|
@ -253,8 +253,8 @@ func (s *Service) MakeAccessToken(clientID, scope, subject string, expiresIn int
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeRefreshToken generates a new refresh token with specific parameters and stores it
|
// MakeRefreshToken generates a new refresh token with specific parameters and stores it
|
||||||
func (s *Service) MakeRefreshToken(clientID, scope, subject string) (string, error) {
|
func (s *Service) MakeRefreshToken(clientID, scope, subject string, expiresIn ...int) (string, error) {
|
||||||
return s.generateRefreshToken(clientID, scope, subject)
|
return s.generateRefreshToken(clientID, scope, subject, expiresIn...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subject converts a userID to a subject using NanoID fingerprint
|
// Subject converts a userID to a subject using NanoID fingerprint
|
||||||
|
|
@ -415,14 +415,14 @@ func (s *Service) revokeAccessToken(accessToken string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateRefreshToken generates and stores a new refresh token with scope and subject
|
// generateRefreshToken generates and stores a new refresh token with scope and subject
|
||||||
func (s *Service) generateRefreshToken(clientID, scope, subject string) (string, error) {
|
func (s *Service) generateRefreshToken(clientID, scope, subject string, expiresIn ...int) (string, error) {
|
||||||
refreshToken, err := s.generateToken("rfk", clientID)
|
refreshToken, err := s.generateToken("rfk", clientID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store refresh token with metadata
|
// Store refresh token with metadata
|
||||||
err = s.storeRefreshTokenWithScope(refreshToken, clientID, scope, subject)
|
err = s.storeRefreshTokenWithScope(refreshToken, clientID, scope, subject, expiresIn...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
@ -529,7 +529,7 @@ func (s *Service) storeRefreshToken(refreshToken, clientID string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// storeRefreshTokenWithScope stores refresh token with metadata including scope and subject
|
// storeRefreshTokenWithScope stores refresh token with metadata including scope and subject
|
||||||
func (s *Service) storeRefreshTokenWithScope(refreshToken, clientID, scope, subject string) error {
|
func (s *Service) storeRefreshTokenWithScope(refreshToken, clientID, scope, subject string, expiresIn ...int) error {
|
||||||
tokenData := map[string]interface{}{
|
tokenData := map[string]interface{}{
|
||||||
"client_id": clientID,
|
"client_id": clientID,
|
||||||
"scope": scope,
|
"scope": scope,
|
||||||
|
|
@ -538,7 +538,12 @@ func (s *Service) storeRefreshTokenWithScope(refreshToken, clientID, scope, subj
|
||||||
"issued_at": time.Now().Unix(),
|
"issued_at": time.Now().Unix(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.store.Set(s.refreshTokenKey(refreshToken), tokenData, s.config.Token.RefreshTokenLifetime)
|
expires := s.config.Token.RefreshTokenLifetime
|
||||||
|
if len(expiresIn) > 0 && expiresIn[0] > 0 {
|
||||||
|
expires = time.Duration(expiresIn[0]) * time.Second
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.store.Set(s.refreshTokenKey(refreshToken), tokenData, expires)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getRefreshTokenData retrieves refresh token data
|
// getRefreshTokenData retrieves refresh token data
|
||||||
|
|
|
||||||
|
|
@ -214,7 +214,18 @@ func authback(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
response.RespondWithSuccess(c, response.StatusOK, loginResponse)
|
// Authorize Cookie
|
||||||
|
accessToken := fmt.Sprintf("%s %s", loginResponse.TokenType, loginResponse.AccessToken)
|
||||||
|
refreshToken := fmt.Sprintf("%s %s", loginResponse.TokenType, loginResponse.RefreshToken)
|
||||||
|
|
||||||
|
// Send Cookie
|
||||||
|
expires := time.Now().Add(time.Duration(loginResponse.ExpiresIn) * time.Second)
|
||||||
|
refreshExpires := time.Now().Add(time.Duration(loginResponse.RefreshTokenExpiresIn) * time.Second)
|
||||||
|
response.SendAccessTokenCookieWithExpiry(c, accessToken, expires)
|
||||||
|
response.SendRefreshTokenCookieWithExpiry(c, refreshToken, refreshExpires)
|
||||||
|
|
||||||
|
// Send IDToken to the client
|
||||||
|
response.RespondWithSuccess(c, response.StatusOK, map[string]interface{}{"id_token": loginResponse.IDToken})
|
||||||
}
|
}
|
||||||
|
|
||||||
// getOAuthAuthorizationURL generates OAuth authorization URL for a provider
|
// getOAuthAuthorizationURL generates OAuth authorization URL for a provider
|
||||||
|
|
|
||||||
|
|
@ -119,17 +119,18 @@ func LoginByUserID(userid string, ip string) (*LoginResponse, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh Token
|
// Refresh Token
|
||||||
refreshToken, err := oauth.OAuth.MakeRefreshToken(yaoClientConfig.ClientID, strings.Join(scopes, " "), subject)
|
refreshToken, err := oauth.OAuth.MakeRefreshToken(yaoClientConfig.ClientID, strings.Join(scopes, " "), subject, yaoClientConfig.RefreshTokenExpiresIn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &LoginResponse{
|
return &LoginResponse{
|
||||||
AccessToken: accessToken,
|
AccessToken: accessToken,
|
||||||
IDToken: oidcToken,
|
IDToken: oidcToken,
|
||||||
RefreshToken: refreshToken,
|
RefreshToken: refreshToken,
|
||||||
ExpiresIn: yaoClientConfig.ExpiresIn,
|
ExpiresIn: yaoClientConfig.ExpiresIn,
|
||||||
TokenType: "Bearer",
|
RefreshTokenExpiresIn: yaoClientConfig.RefreshTokenExpiresIn,
|
||||||
Scope: strings.Join(scopes, " "),
|
TokenType: "Bearer",
|
||||||
|
Scope: strings.Join(scopes, " "),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -156,7 +156,8 @@ func registerClient(clientID string) (*YaoClientConfig, error) {
|
||||||
var clientConfig *YaoClientConfig = &YaoClientConfig{}
|
var clientConfig *YaoClientConfig = &YaoClientConfig{}
|
||||||
clientConfig.ClientID = response.ClientID
|
clientConfig.ClientID = response.ClientID
|
||||||
clientConfig.ClientSecret = response.ClientSecret
|
clientConfig.ClientSecret = response.ClientSecret
|
||||||
clientConfig.ExpiresIn = 3600 * 24 // 24 hours
|
clientConfig.ExpiresIn = 3600 * 24 // 24 hours
|
||||||
|
clientConfig.RefreshTokenExpiresIn = 3600 * 24 * 30 // 30 days
|
||||||
clientConfig.Scopes = []string{"openid", "profile", "email"}
|
clientConfig.Scopes = []string{"openid", "profile", "email"}
|
||||||
return clientConfig, nil
|
return clientConfig, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,10 +66,11 @@ type RegisterConfig struct {
|
||||||
|
|
||||||
// YaoClientConfig represents the Yao OpenAPI Client config
|
// YaoClientConfig represents the Yao OpenAPI Client config
|
||||||
type YaoClientConfig struct {
|
type YaoClientConfig struct {
|
||||||
ClientID string `json:"client_id,omitempty"`
|
ClientID string `json:"client_id,omitempty"`
|
||||||
ClientSecret string `json:"client_secret,omitempty"`
|
ClientSecret string `json:"client_secret,omitempty"`
|
||||||
Scopes []string `json:"scopes,omitempty"` // Default scopes if not set in the provider config
|
Scopes []string `json:"scopes,omitempty"` // Default scopes if not set in the provider config
|
||||||
ExpiresIn int `json:"expires_in,omitempty"` // Default expires in for the access token (optional) in seconds
|
ExpiresIn int `json:"expires_in,omitempty"` // Default expires in for the access token (optional) in seconds
|
||||||
|
RefreshTokenExpiresIn int `json:"refresh_token_expires_in,omitempty"` // Default expires in for the refresh token (optional) in seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
// Provider represents a third party login provider
|
// Provider represents a third party login provider
|
||||||
|
|
@ -161,12 +162,13 @@ type OIDCAddress = oauthtypes.OIDCAddress
|
||||||
|
|
||||||
// LoginResponse represents the response for login
|
// LoginResponse represents the response for login
|
||||||
type LoginResponse struct {
|
type LoginResponse struct {
|
||||||
AccessToken string `json:"access_token"`
|
AccessToken string `json:"access_token"`
|
||||||
IDToken string `json:"id_token,omitempty"`
|
IDToken string `json:"id_token,omitempty"`
|
||||||
RefreshToken string `json:"refresh_token,omitempty"`
|
RefreshToken string `json:"refresh_token,omitempty"`
|
||||||
ExpiresIn int `json:"expires_in,omitempty"`
|
ExpiresIn int `json:"expires_in,omitempty"`
|
||||||
TokenType string `json:"token_type,omitempty"`
|
RefreshTokenExpiresIn int `json:"refresh_token_expires_in,omitempty"`
|
||||||
Scope string `json:"scope,omitempty"`
|
TokenType string `json:"token_type,omitempty"`
|
||||||
|
Scope string `json:"scope,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Built-in preset mapping types
|
// Built-in preset mapping types
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue