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:
scaletech-milan 2023-11-21 13:08:32 +05:30 committed by GitHub
parent fe4c693324
commit de5c18b60f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 36 additions and 23 deletions

View File

@ -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({
<Fragment>
<h1 style={{ textAlign: 'center' }}>Sign Up</h1>
<br />
<AuthorizerSocialLogin urlProps={urlProps} />
<AuthorizerSignup urlProps={urlProps} />
<FooterContent>
Already have an account? <Link to="/app"> Login</Link>

View File

@ -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"
)

View File

@ -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
}