From de5c18b60fe7e40db07f7647dcb2693bbcf8b3a9 Mon Sep 17 00:00:00 2001 From: scaletech-milan <112945284+scaletech-milan@users.noreply.github.com> Date: Tue, 21 Nov 2023 13:08:32 +0530 Subject: [PATCH] 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 --- app/src/pages/signup.tsx | 3 +- server/constants/oauth2.go | 3 ++ server/handlers/authorize.go | 53 +++++++++++++++++++++--------------- 3 files changed, 36 insertions(+), 23 deletions(-) diff --git a/app/src/pages/signup.tsx b/app/src/pages/signup.tsx index 57fd76c..3b235f4 100644 --- a/app/src/pages/signup.tsx +++ b/app/src/pages/signup.tsx @@ -1,5 +1,5 @@ import React, { Fragment } from 'react'; -import { AuthorizerSignup } from '@authorizerdev/authorizer-react'; +import { AuthorizerSignup, AuthorizerSocialLogin } from '@authorizerdev/authorizer-react'; import styled from 'styled-components'; import { Link } from 'react-router-dom'; @@ -19,6 +19,7 @@ export default function SignUp({

Sign Up


+ Already have an account? Login diff --git a/server/constants/oauth2.go b/server/constants/oauth2.go index f3e0a67..78a86ff 100644 --- a/server/constants/oauth2.go +++ b/server/constants/oauth2.go @@ -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" ) diff --git a/server/handlers/authorize.go b/server/handlers/authorize.go index f4c2b5e..46559dd 100644 --- a/server/handlers/authorize.go +++ b/server/handlers/authorize.go @@ -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 }