Enhance OAuth state management and validation in Signin API
- Introduced UUID-based state parameter generation for improved uniqueness and security in OAuth flows. - Added validation for user-provided state parameters to ensure they conform to UUID format, with warnings included in the response. - Refactored the generateRandomState function to utilize UUID generation instead of cryptographic random bytes. - Updated OAuthAuthorizationURLResponse to include optional warnings about state format issues.
This commit is contained in:
parent
be1acd324e
commit
2f0a31894c
3 changed files with 24 additions and 30 deletions
|
|
@ -1,15 +1,15 @@
|
||||||
package signin
|
package signin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/yaoapp/gou/session"
|
"github.com/yaoapp/gou/session"
|
||||||
"github.com/yaoapp/gou/store"
|
"github.com/yaoapp/gou/store"
|
||||||
"github.com/yaoapp/kun/log"
|
"github.com/yaoapp/kun/log"
|
||||||
|
|
@ -256,6 +256,9 @@ func getOAuthAuthorizationURL(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if state is provided by user and validate format
|
||||||
|
var warnings []string
|
||||||
|
|
||||||
// Generate state if not provided
|
// Generate state if not provided
|
||||||
if state == "" {
|
if state == "" {
|
||||||
var err error
|
var err error
|
||||||
|
|
@ -268,6 +271,11 @@ func getOAuthAuthorizationURL(c *gin.Context) {
|
||||||
response.RespondWithError(c, response.StatusInternalServerError, errorResp)
|
response.RespondWithError(c, response.StatusInternalServerError, errorResp)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// User provided state - check if it's in UUID format
|
||||||
|
if !isValidUUID(state) {
|
||||||
|
warnings = append(warnings, "State parameter is not in UUID format. For better uniqueness and security, consider using UUID format.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set default redirect URI if not provided
|
// Set default redirect URI if not provided
|
||||||
|
|
@ -347,17 +355,21 @@ func getOAuthAuthorizationURL(c *gin.Context) {
|
||||||
response.RespondWithSuccess(c, response.StatusOK, &OAuthAuthorizationURLResponse{
|
response.RespondWithSuccess(c, response.StatusOK, &OAuthAuthorizationURLResponse{
|
||||||
AuthorizationURL: authorizationURL,
|
AuthorizationURL: authorizationURL,
|
||||||
State: state,
|
State: state,
|
||||||
|
Warnings: warnings,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateRandomState generates a cryptographically secure random state parameter
|
// generateRandomState generates a UUID-based state parameter for better uniqueness
|
||||||
func generateRandomState() (string, error) {
|
func generateRandomState() (string, error) {
|
||||||
bytes := make([]byte, 16)
|
u := uuid.New()
|
||||||
_, err := rand.Read(bytes)
|
return u.String(), nil
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
}
|
||||||
return hex.EncodeToString(bytes), nil
|
|
||||||
|
// isValidUUID checks if a string is a valid UUID format
|
||||||
|
func isValidUUID(s string) bool {
|
||||||
|
// UUID v4 format: 8-4-4-4-12 hexadecimal characters
|
||||||
|
uuidRegex := regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`)
|
||||||
|
return uuidRegex.MatchString(strings.ToLower(s))
|
||||||
}
|
}
|
||||||
|
|
||||||
// getScheme returns the request scheme (http or https)
|
// getScheme returns the request scheme (http or https)
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ import (
|
||||||
"github.com/yaoapp/gou/application"
|
"github.com/yaoapp/gou/application"
|
||||||
"github.com/yaoapp/gou/http"
|
"github.com/yaoapp/gou/http"
|
||||||
"github.com/yaoapp/kun/log"
|
"github.com/yaoapp/kun/log"
|
||||||
"github.com/yaoapp/kun/utils"
|
|
||||||
oauthtypes "github.com/yaoapp/yao/openapi/oauth/types"
|
oauthtypes "github.com/yaoapp/yao/openapi/oauth/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -411,20 +410,6 @@ func (p *Provider) getUserInfoFromEndpoint(accessToken string, tokenType string)
|
||||||
return userInfo, nil
|
return userInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getUserInfoFromIDToken extracts user info from ID token (JWT) with signature verification
|
|
||||||
func (p *Provider) getUserInfoFromIDToken(idToken string) (*oauthtypes.OIDCUserInfo, error) {
|
|
||||||
// Verify JWT signature and get raw claims
|
|
||||||
rawClaims, err := p.verifyIDTokenAndGetClaims(idToken)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to verify ID token: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map the raw JWT claims to our standard user info structure
|
|
||||||
userInfo := p.mapUserInfoResponse(rawClaims)
|
|
||||||
|
|
||||||
return userInfo, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// verifyIDTokenAndGetClaims verifies ID token signature and returns raw claims for user info mapping
|
// verifyIDTokenAndGetClaims verifies ID token signature and returns raw claims for user info mapping
|
||||||
func (p *Provider) verifyIDTokenAndGetClaims(idToken string) (map[string]interface{}, error) {
|
func (p *Provider) verifyIDTokenAndGetClaims(idToken string) (map[string]interface{}, error) {
|
||||||
// Parse token to get header for key ID
|
// Parse token to get header for key ID
|
||||||
|
|
@ -457,10 +442,6 @@ func (p *Provider) verifyIDTokenAndGetClaims(idToken string) (map[string]interfa
|
||||||
return nil, fmt.Errorf("invalid JWT token")
|
return nil, fmt.Errorf("invalid JWT token")
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("--- TEST ---")
|
|
||||||
utils.Dump(token.Claims)
|
|
||||||
fmt.Println("---------------")
|
|
||||||
|
|
||||||
// Extract claims
|
// Extract claims
|
||||||
claims, ok := token.Claims.(jwt.MapClaims)
|
claims, ok := token.Claims.(jwt.MapClaims)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,7 @@ type Endpoints struct {
|
||||||
type OAuthAuthorizationURLResponse struct {
|
type OAuthAuthorizationURLResponse struct {
|
||||||
AuthorizationURL string `json:"authorization_url"`
|
AuthorizationURL string `json:"authorization_url"`
|
||||||
State string `json:"state"`
|
State string `json:"state"`
|
||||||
|
Warnings []string `json:"warnings,omitempty"` // Optional warnings about state format or other issues
|
||||||
}
|
}
|
||||||
|
|
||||||
// OAuthCallbackResponse represents the response for OAuth callback
|
// OAuthCallbackResponse represents the response for OAuth callback
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue