-
This commit is contained in:
@ -124,20 +124,11 @@ type VerifyResponse struct {
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// TokenDeliveryMode specifies how tokens should be delivered in redirect flows
|
||||
type TokenDeliveryMode string
|
||||
|
||||
const (
|
||||
TokenDeliveryCookie TokenDeliveryMode = "cookie" // Token in secure cookie (default)
|
||||
TokenDeliveryQuery TokenDeliveryMode = "query" // Token in query parameter (for integrations)
|
||||
)
|
||||
|
||||
// LoginRequest represents a user login request
|
||||
type LoginRequest struct {
|
||||
AppID string `json:"app_id" validate:"required"`
|
||||
Permissions []string `json:"permissions,omitempty"`
|
||||
RedirectURI string `json:"redirect_uri,omitempty"`
|
||||
TokenDelivery TokenDeliveryMode `json:"token_delivery,omitempty"` // How to deliver token in redirect flows
|
||||
AppID string `json:"app_id" validate:"required"`
|
||||
Permissions []string `json:"permissions,omitempty"`
|
||||
RedirectURI string `json:"redirect_uri,omitempty"`
|
||||
}
|
||||
|
||||
// LoginResponse represents a user login response
|
||||
|
||||
@ -35,13 +35,12 @@ type AuthHandler struct {
|
||||
|
||||
// LoginPageData represents data passed to the login HTML template
|
||||
type LoginPageData struct {
|
||||
Token string
|
||||
TokenJSON template.JS
|
||||
RedirectURLJSON template.JS
|
||||
TokenDeliveryJSON template.JS
|
||||
ExpiresAt string
|
||||
AppID string
|
||||
UserID string
|
||||
Token string
|
||||
TokenJSON template.JS
|
||||
RedirectURLJSON template.JS
|
||||
ExpiresAt string
|
||||
AppID string
|
||||
UserID string
|
||||
}
|
||||
|
||||
// NewAuthHandler creates a new auth handler
|
||||
@ -92,7 +91,6 @@ func (h *AuthHandler) Login(c *gin.Context) {
|
||||
// Handle HTML request (GET or POST with form data)
|
||||
req.AppID = c.Query("app_id")
|
||||
req.RedirectURI = c.Query("redirect_uri")
|
||||
req.TokenDelivery = domain.TokenDeliveryMode(c.DefaultQuery("token_delivery", string(domain.TokenDeliveryQuery)))
|
||||
|
||||
// Parse permissions from query parameter (comma-separated)
|
||||
if perms := c.Query("permissions"); perms != "" {
|
||||
@ -142,56 +140,12 @@ func (h *AuthHandler) Login(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// Handle redirect flows
|
||||
tokenDelivery := req.TokenDelivery
|
||||
if tokenDelivery == "" {
|
||||
// Default to query delivery for redirects (external apps need token in URL)
|
||||
// Only use cookie delivery if explicitly specified
|
||||
tokenDelivery = domain.TokenDeliveryQuery
|
||||
}
|
||||
|
||||
h.logger.Debug("Token delivery mode", zap.String("mode", string(tokenDelivery)))
|
||||
|
||||
// Generate a secure state parameter for CSRF protection
|
||||
state := h.generateSecureState(userContext.UserID, req.AppID)
|
||||
// Handle redirect flows - always deliver token via query parameter
|
||||
var redirectURL string
|
||||
|
||||
switch tokenDelivery {
|
||||
case domain.TokenDeliveryQuery:
|
||||
// Deliver token via query parameter (for integrations like VS Code)
|
||||
if req.RedirectURI != "" {
|
||||
redirectURL = req.RedirectURI + "?token=" + token + "&state=" + state
|
||||
}
|
||||
|
||||
case domain.TokenDeliveryCookie:
|
||||
// Deliver token via secure cookie (default, more secure)
|
||||
c.SetSameSite(http.SameSiteStrictMode)
|
||||
|
||||
// In development mode, make cookie accessible to JavaScript for testing
|
||||
// In production, keep HTTP-only for security
|
||||
httpOnly := !h.config.IsDevelopment()
|
||||
secure := !h.config.IsDevelopment() // Only require HTTPS in production
|
||||
|
||||
c.SetCookie(
|
||||
"auth_token", // name
|
||||
token, // value
|
||||
604800, // maxAge (7 days)
|
||||
"/", // path
|
||||
"", // domain (empty for current domain)
|
||||
secure, // secure (HTTPS only in production)
|
||||
httpOnly, // httpOnly (no JavaScript access in production)
|
||||
)
|
||||
|
||||
// Redirect without token in URL for security
|
||||
if req.RedirectURI != "" {
|
||||
redirectURL = req.RedirectURI + "?state=" + state
|
||||
}
|
||||
|
||||
default:
|
||||
// Invalid delivery mode, default to cookie
|
||||
if req.RedirectURI != "" {
|
||||
redirectURL = req.RedirectURI + "?state=" + state
|
||||
}
|
||||
if req.RedirectURI != "" {
|
||||
// Generate a secure state parameter for CSRF protection
|
||||
state := h.generateSecureState(userContext.UserID, req.AppID)
|
||||
redirectURL = req.RedirectURI + "?token=" + token + "&state=" + state
|
||||
}
|
||||
|
||||
// Return appropriate response format
|
||||
@ -202,12 +156,12 @@ func (h *AuthHandler) Login(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, response)
|
||||
} else {
|
||||
// Render HTML page
|
||||
h.renderLoginPage(c, token, redirectURL, string(tokenDelivery), userContext.UserID, req.AppID)
|
||||
h.renderLoginPage(c, token, redirectURL, userContext.UserID, req.AppID)
|
||||
}
|
||||
}
|
||||
|
||||
// renderLoginPage renders the HTML login page with token information
|
||||
func (h *AuthHandler) renderLoginPage(c *gin.Context, token, redirectURL, tokenDelivery, userID, appID string) {
|
||||
func (h *AuthHandler) renderLoginPage(c *gin.Context, token, redirectURL, userID, appID string) {
|
||||
if h.loginTemplate == nil {
|
||||
// Fallback to JSON if template not available
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
@ -223,16 +177,14 @@ func (h *AuthHandler) renderLoginPage(c *gin.Context, token, redirectURL, tokenD
|
||||
// Prepare template data
|
||||
tokenJSON, _ := json.Marshal(token)
|
||||
redirectURLJSON, _ := json.Marshal(redirectURL)
|
||||
tokenDeliveryJSON, _ := json.Marshal(tokenDelivery)
|
||||
|
||||
data := LoginPageData{
|
||||
Token: token,
|
||||
TokenJSON: template.JS(tokenJSON),
|
||||
RedirectURLJSON: template.JS(redirectURLJSON),
|
||||
TokenDeliveryJSON: template.JS(tokenDeliveryJSON),
|
||||
ExpiresAt: time.Now().Add(7 * 24 * time.Hour).Format("Jan 2, 2006 at 3:04 PM MST"),
|
||||
AppID: appID,
|
||||
UserID: userID,
|
||||
Token: token,
|
||||
TokenJSON: template.JS(tokenJSON),
|
||||
RedirectURLJSON: template.JS(redirectURLJSON),
|
||||
ExpiresAt: time.Now().Add(7 * 24 * time.Hour).Format("Jan 2, 2006 at 3:04 PM MST"),
|
||||
AppID: appID,
|
||||
UserID: userID,
|
||||
}
|
||||
|
||||
c.Header("Content-Type", "text/html; charset=utf-8")
|
||||
|
||||
@ -157,7 +157,6 @@
|
||||
// Token and redirect information from server
|
||||
const token = {{.TokenJSON}};
|
||||
const redirectURL = {{.RedirectURLJSON}};
|
||||
const tokenDelivery = {{.TokenDeliveryJSON}};
|
||||
|
||||
// Elements
|
||||
const loadingDiv = document.getElementById('loading');
|
||||
@ -229,13 +228,7 @@
|
||||
setTimeout(performRedirect, 1000);
|
||||
});
|
||||
|
||||
// Handle cookie-based token delivery
|
||||
if (tokenDelivery === 'cookie') {
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const message = document.querySelector('.redirect-info');
|
||||
message.innerHTML += '<br><strong>Delivery method:</strong> Secure cookie (auth_token)';
|
||||
});
|
||||
}
|
||||
// Token is always delivered via query parameter
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user