Feat: add screen_hint param in /authorize api for explicit signup redirection (#420)
* Feat: - Introduce screen_hint param in /authorize for explicit signup redirection * Feat: - Declare variable for base path and signup path - Add social login on signup page * Refactor: - Update variable name for screen hint param
This commit is contained in:
@@ -16,4 +16,7 @@ const (
|
||||
ResponseTypeToken = "token"
|
||||
// For the Implicit grant of id_token, use response_type=id_token to include an identifier token.
|
||||
ResponseTypeIDToken = "id_token"
|
||||
|
||||
// Constant indicating the "signup" screen hint for customizing authentication process and redirect to a signup page.
|
||||
ScreenHintSignUp = "signup"
|
||||
)
|
||||
|
@@ -55,6 +55,8 @@ import (
|
||||
const (
|
||||
authorizeWebMessageTemplate = "authorize_web_message.tmpl"
|
||||
authorizeFormPostTemplate = "authorize_form_post.tmpl"
|
||||
baseAppPath = "/app"
|
||||
signupPath = "/app/signup"
|
||||
)
|
||||
|
||||
// AuthorizeHandler is the handler for the /authorize route
|
||||
@@ -74,6 +76,7 @@ func AuthorizeHandler() gin.HandlerFunc {
|
||||
clientID := strings.TrimSpace(gc.Query("client_id"))
|
||||
responseMode := strings.TrimSpace(gc.Query("response_mode"))
|
||||
nonce := strings.TrimSpace(gc.Query("nonce"))
|
||||
screenHint := strings.TrimSpace(gc.Query("screen_hint"))
|
||||
|
||||
var scope []string
|
||||
if scopeString == "" {
|
||||
@@ -120,27 +123,33 @@ func AuthorizeHandler() gin.HandlerFunc {
|
||||
|
||||
// TODO add state with timeout
|
||||
// used for response mode query or fragment
|
||||
loginState := "state=" + state + "&scope=" + strings.Join(scope, " ") + "&redirect_uri=" + redirectURI
|
||||
authState := "state=" + state + "&scope=" + strings.Join(scope, " ") + "&redirect_uri=" + redirectURI
|
||||
if responseType == constants.ResponseTypeCode {
|
||||
loginState += "&code=" + code
|
||||
authState += "&code=" + code
|
||||
if err := memorystore.Provider.SetState(state, code+"@@"+codeChallenge); err != nil {
|
||||
log.Debug("Error setting temp code", err)
|
||||
}
|
||||
} else {
|
||||
loginState += "&nonce=" + nonce
|
||||
authState += "&nonce=" + nonce
|
||||
if err := memorystore.Provider.SetState(state, nonce); err != nil {
|
||||
log.Debug("Error setting temp code", err)
|
||||
}
|
||||
}
|
||||
|
||||
loginURL := "/app?" + loginState
|
||||
authURL := baseAppPath + "?" + authState
|
||||
|
||||
if responseMode == constants.ResponseModeFragment {
|
||||
loginURL = "/app#" + loginState
|
||||
if screenHint == constants.ScreenHintSignUp {
|
||||
authURL = signupPath + "?" + authState
|
||||
}
|
||||
|
||||
if responseMode == constants.ResponseModeFragment && screenHint == constants.ScreenHintSignUp {
|
||||
authURL = signupPath + "#" + authState
|
||||
} else if responseMode == constants.ResponseModeFragment {
|
||||
authURL = baseAppPath + "#" + authState
|
||||
}
|
||||
|
||||
if responseType == constants.ResponseTypeCode && codeChallenge == "" {
|
||||
handleResponse(gc, responseMode, loginURL, redirectURI, map[string]interface{}{
|
||||
handleResponse(gc, responseMode, authURL, redirectURI, map[string]interface{}{
|
||||
"type": "authorization_response",
|
||||
"response": map[string]interface{}{
|
||||
"error": "code_challenge_required",
|
||||
@@ -160,7 +169,7 @@ func AuthorizeHandler() gin.HandlerFunc {
|
||||
sessionToken, err := cookie.GetSession(gc)
|
||||
if err != nil {
|
||||
log.Debug("GetSession failed: ", err)
|
||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
||||
handleResponse(gc, responseMode, authURL, redirectURI, loginError, http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -168,7 +177,7 @@ func AuthorizeHandler() gin.HandlerFunc {
|
||||
claims, err := token.ValidateBrowserSession(gc, sessionToken)
|
||||
if err != nil {
|
||||
log.Debug("ValidateBrowserSession failed: ", err)
|
||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
||||
handleResponse(gc, responseMode, authURL, redirectURI, loginError, http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -176,7 +185,7 @@ func AuthorizeHandler() gin.HandlerFunc {
|
||||
user, err := db.Provider.GetUserByID(gc, userID)
|
||||
if err != nil {
|
||||
log.Debug("GetUserByID failed: ", err)
|
||||
handleResponse(gc, responseMode, loginURL, redirectURI, map[string]interface{}{
|
||||
handleResponse(gc, responseMode, authURL, redirectURI, map[string]interface{}{
|
||||
"type": "authorization_response",
|
||||
"response": map[string]interface{}{
|
||||
"error": "signup_required",
|
||||
@@ -197,27 +206,27 @@ func AuthorizeHandler() gin.HandlerFunc {
|
||||
newSessionTokenData, newSessionToken, newSessionExpiresAt, err := token.CreateSessionToken(user, nonce, claims.Roles, scope, claims.LoginMethod)
|
||||
if err != nil {
|
||||
log.Debug("CreateSessionToken failed: ", err)
|
||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
||||
handleResponse(gc, responseMode, authURL, redirectURI, loginError, http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: add state with timeout
|
||||
// if err := memorystore.Provider.SetState(codeChallenge, code+"@"+newSessionToken); err != nil {
|
||||
// log.Debug("SetState failed: ", err)
|
||||
// handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
||||
// handleResponse(gc, responseMode, authURL, redirectURI, loginError, http.StatusOK)
|
||||
// return
|
||||
// }
|
||||
|
||||
// TODO: add state with timeout
|
||||
if err := memorystore.Provider.SetState(code, codeChallenge+"@@"+newSessionToken); err != nil {
|
||||
log.Debug("SetState failed: ", err)
|
||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
||||
handleResponse(gc, responseMode, authURL, redirectURI, loginError, http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+newSessionTokenData.Nonce, newSessionToken, newSessionExpiresAt); err != nil {
|
||||
log.Debug("SetUserSession failed: ", err)
|
||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
||||
handleResponse(gc, responseMode, authURL, redirectURI, loginError, http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -251,7 +260,7 @@ func AuthorizeHandler() gin.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
handleResponse(gc, responseMode, loginURL, redirectURI, map[string]interface{}{
|
||||
handleResponse(gc, responseMode, authURL, redirectURI, map[string]interface{}{
|
||||
"type": "authorization_response",
|
||||
"response": map[string]interface{}{
|
||||
"code": code,
|
||||
@@ -267,19 +276,19 @@ func AuthorizeHandler() gin.HandlerFunc {
|
||||
authToken, err := token.CreateAuthToken(gc, user, claims.Roles, scope, claims.LoginMethod, nonce, "")
|
||||
if err != nil {
|
||||
log.Debug("CreateAuthToken failed: ", err)
|
||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
||||
handleResponse(gc, responseMode, authURL, redirectURI, loginError, http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+nonce, authToken.FingerPrintHash, authToken.SessionTokenExpiresAt); err != nil {
|
||||
log.Debug("SetUserSession failed: ", err)
|
||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
||||
handleResponse(gc, responseMode, authURL, redirectURI, loginError, http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+nonce, authToken.AccessToken.Token, authToken.AccessToken.ExpiresAt); err != nil {
|
||||
log.Debug("SetUserSession failed: ", err)
|
||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
||||
handleResponse(gc, responseMode, authURL, redirectURI, loginError, http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -322,14 +331,14 @@ func AuthorizeHandler() gin.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
handleResponse(gc, responseMode, loginURL, redirectURI, map[string]interface{}{
|
||||
handleResponse(gc, responseMode, authURL, redirectURI, map[string]interface{}{
|
||||
"type": "authorization_response",
|
||||
"response": res,
|
||||
}, http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
||||
handleResponse(gc, responseMode, authURL, redirectURI, loginError, http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,14 +361,14 @@ func validateAuthorizeRequest(responseType, responseMode, clientID, state, codeC
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleResponse(gc *gin.Context, responseMode, loginURI, redirectURI string, data map[string]interface{}, httpStatusCode int) {
|
||||
func handleResponse(gc *gin.Context, responseMode, authURI, redirectURI string, data map[string]interface{}, httpStatusCode int) {
|
||||
isAuthenticationRequired := false
|
||||
if _, ok := data["response"].(map[string]interface{})["error"]; ok {
|
||||
isAuthenticationRequired = true
|
||||
}
|
||||
|
||||
if isAuthenticationRequired && responseMode != constants.ResponseModeWebMessage {
|
||||
gc.Redirect(http.StatusFound, loginURI)
|
||||
gc.Redirect(http.StatusFound, authURI)
|
||||
return
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user