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:
parent
fe4c693324
commit
de5c18b60f
|
@ -1,5 +1,5 @@
|
||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import { AuthorizerSignup } from '@authorizerdev/authorizer-react';
|
import { AuthorizerSignup, AuthorizerSocialLogin } from '@authorizerdev/authorizer-react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ export default function SignUp({
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<h1 style={{ textAlign: 'center' }}>Sign Up</h1>
|
<h1 style={{ textAlign: 'center' }}>Sign Up</h1>
|
||||||
<br />
|
<br />
|
||||||
|
<AuthorizerSocialLogin urlProps={urlProps} />
|
||||||
<AuthorizerSignup urlProps={urlProps} />
|
<AuthorizerSignup urlProps={urlProps} />
|
||||||
<FooterContent>
|
<FooterContent>
|
||||||
Already have an account? <Link to="/app"> Login</Link>
|
Already have an account? <Link to="/app"> Login</Link>
|
||||||
|
|
|
@ -16,4 +16,7 @@ const (
|
||||||
ResponseTypeToken = "token"
|
ResponseTypeToken = "token"
|
||||||
// For the Implicit grant of id_token, use response_type=id_token to include an identifier token.
|
// For the Implicit grant of id_token, use response_type=id_token to include an identifier token.
|
||||||
ResponseTypeIDToken = "id_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 (
|
const (
|
||||||
authorizeWebMessageTemplate = "authorize_web_message.tmpl"
|
authorizeWebMessageTemplate = "authorize_web_message.tmpl"
|
||||||
authorizeFormPostTemplate = "authorize_form_post.tmpl"
|
authorizeFormPostTemplate = "authorize_form_post.tmpl"
|
||||||
|
baseAppPath = "/app"
|
||||||
|
signupPath = "/app/signup"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AuthorizeHandler is the handler for the /authorize route
|
// AuthorizeHandler is the handler for the /authorize route
|
||||||
|
@ -74,6 +76,7 @@ func AuthorizeHandler() gin.HandlerFunc {
|
||||||
clientID := strings.TrimSpace(gc.Query("client_id"))
|
clientID := strings.TrimSpace(gc.Query("client_id"))
|
||||||
responseMode := strings.TrimSpace(gc.Query("response_mode"))
|
responseMode := strings.TrimSpace(gc.Query("response_mode"))
|
||||||
nonce := strings.TrimSpace(gc.Query("nonce"))
|
nonce := strings.TrimSpace(gc.Query("nonce"))
|
||||||
|
screenHint := strings.TrimSpace(gc.Query("screen_hint"))
|
||||||
|
|
||||||
var scope []string
|
var scope []string
|
||||||
if scopeString == "" {
|
if scopeString == "" {
|
||||||
|
@ -120,27 +123,33 @@ func AuthorizeHandler() gin.HandlerFunc {
|
||||||
|
|
||||||
// TODO add state with timeout
|
// TODO add state with timeout
|
||||||
// used for response mode query or fragment
|
// 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 {
|
if responseType == constants.ResponseTypeCode {
|
||||||
loginState += "&code=" + code
|
authState += "&code=" + code
|
||||||
if err := memorystore.Provider.SetState(state, code+"@@"+codeChallenge); err != nil {
|
if err := memorystore.Provider.SetState(state, code+"@@"+codeChallenge); err != nil {
|
||||||
log.Debug("Error setting temp code", err)
|
log.Debug("Error setting temp code", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
loginState += "&nonce=" + nonce
|
authState += "&nonce=" + nonce
|
||||||
if err := memorystore.Provider.SetState(state, nonce); err != nil {
|
if err := memorystore.Provider.SetState(state, nonce); err != nil {
|
||||||
log.Debug("Error setting temp code", err)
|
log.Debug("Error setting temp code", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loginURL := "/app?" + loginState
|
authURL := baseAppPath + "?" + authState
|
||||||
|
|
||||||
if responseMode == constants.ResponseModeFragment {
|
if screenHint == constants.ScreenHintSignUp {
|
||||||
loginURL = "/app#" + loginState
|
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 == "" {
|
if responseType == constants.ResponseTypeCode && codeChallenge == "" {
|
||||||
handleResponse(gc, responseMode, loginURL, redirectURI, map[string]interface{}{
|
handleResponse(gc, responseMode, authURL, redirectURI, map[string]interface{}{
|
||||||
"type": "authorization_response",
|
"type": "authorization_response",
|
||||||
"response": map[string]interface{}{
|
"response": map[string]interface{}{
|
||||||
"error": "code_challenge_required",
|
"error": "code_challenge_required",
|
||||||
|
@ -160,7 +169,7 @@ func AuthorizeHandler() gin.HandlerFunc {
|
||||||
sessionToken, err := cookie.GetSession(gc)
|
sessionToken, err := cookie.GetSession(gc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("GetSession failed: ", err)
|
log.Debug("GetSession failed: ", err)
|
||||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
handleResponse(gc, responseMode, authURL, redirectURI, loginError, http.StatusOK)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,7 +177,7 @@ func AuthorizeHandler() gin.HandlerFunc {
|
||||||
claims, err := token.ValidateBrowserSession(gc, sessionToken)
|
claims, err := token.ValidateBrowserSession(gc, sessionToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("ValidateBrowserSession failed: ", err)
|
log.Debug("ValidateBrowserSession failed: ", err)
|
||||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
handleResponse(gc, responseMode, authURL, redirectURI, loginError, http.StatusOK)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,7 +185,7 @@ func AuthorizeHandler() gin.HandlerFunc {
|
||||||
user, err := db.Provider.GetUserByID(gc, userID)
|
user, err := db.Provider.GetUserByID(gc, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("GetUserByID failed: ", err)
|
log.Debug("GetUserByID failed: ", err)
|
||||||
handleResponse(gc, responseMode, loginURL, redirectURI, map[string]interface{}{
|
handleResponse(gc, responseMode, authURL, redirectURI, map[string]interface{}{
|
||||||
"type": "authorization_response",
|
"type": "authorization_response",
|
||||||
"response": map[string]interface{}{
|
"response": map[string]interface{}{
|
||||||
"error": "signup_required",
|
"error": "signup_required",
|
||||||
|
@ -197,27 +206,27 @@ func AuthorizeHandler() gin.HandlerFunc {
|
||||||
newSessionTokenData, newSessionToken, newSessionExpiresAt, err := token.CreateSessionToken(user, nonce, claims.Roles, scope, claims.LoginMethod)
|
newSessionTokenData, newSessionToken, newSessionExpiresAt, err := token.CreateSessionToken(user, nonce, claims.Roles, scope, claims.LoginMethod)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("CreateSessionToken failed: ", err)
|
log.Debug("CreateSessionToken failed: ", err)
|
||||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
handleResponse(gc, responseMode, authURL, redirectURI, loginError, http.StatusOK)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add state with timeout
|
// TODO: add state with timeout
|
||||||
// if err := memorystore.Provider.SetState(codeChallenge, code+"@"+newSessionToken); err != nil {
|
// if err := memorystore.Provider.SetState(codeChallenge, code+"@"+newSessionToken); err != nil {
|
||||||
// log.Debug("SetState failed: ", err)
|
// log.Debug("SetState failed: ", err)
|
||||||
// handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
// handleResponse(gc, responseMode, authURL, redirectURI, loginError, http.StatusOK)
|
||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// TODO: add state with timeout
|
// TODO: add state with timeout
|
||||||
if err := memorystore.Provider.SetState(code, codeChallenge+"@@"+newSessionToken); err != nil {
|
if err := memorystore.Provider.SetState(code, codeChallenge+"@@"+newSessionToken); err != nil {
|
||||||
log.Debug("SetState failed: ", err)
|
log.Debug("SetState failed: ", err)
|
||||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
handleResponse(gc, responseMode, authURL, redirectURI, loginError, http.StatusOK)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+newSessionTokenData.Nonce, newSessionToken, newSessionExpiresAt); err != nil {
|
if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+newSessionTokenData.Nonce, newSessionToken, newSessionExpiresAt); err != nil {
|
||||||
log.Debug("SetUserSession failed: ", err)
|
log.Debug("SetUserSession failed: ", err)
|
||||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
handleResponse(gc, responseMode, authURL, redirectURI, loginError, http.StatusOK)
|
||||||
return
|
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",
|
"type": "authorization_response",
|
||||||
"response": map[string]interface{}{
|
"response": map[string]interface{}{
|
||||||
"code": code,
|
"code": code,
|
||||||
|
@ -267,19 +276,19 @@ func AuthorizeHandler() gin.HandlerFunc {
|
||||||
authToken, err := token.CreateAuthToken(gc, user, claims.Roles, scope, claims.LoginMethod, nonce, "")
|
authToken, err := token.CreateAuthToken(gc, user, claims.Roles, scope, claims.LoginMethod, nonce, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("CreateAuthToken failed: ", err)
|
log.Debug("CreateAuthToken failed: ", err)
|
||||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
handleResponse(gc, responseMode, authURL, redirectURI, loginError, http.StatusOK)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+nonce, authToken.FingerPrintHash, authToken.SessionTokenExpiresAt); err != nil {
|
if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+nonce, authToken.FingerPrintHash, authToken.SessionTokenExpiresAt); err != nil {
|
||||||
log.Debug("SetUserSession failed: ", err)
|
log.Debug("SetUserSession failed: ", err)
|
||||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
handleResponse(gc, responseMode, authURL, redirectURI, loginError, http.StatusOK)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+nonce, authToken.AccessToken.Token, authToken.AccessToken.ExpiresAt); err != nil {
|
if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+nonce, authToken.AccessToken.Token, authToken.AccessToken.ExpiresAt); err != nil {
|
||||||
log.Debug("SetUserSession failed: ", err)
|
log.Debug("SetUserSession failed: ", err)
|
||||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
handleResponse(gc, responseMode, authURL, redirectURI, loginError, http.StatusOK)
|
||||||
return
|
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",
|
"type": "authorization_response",
|
||||||
"response": res,
|
"response": res,
|
||||||
}, http.StatusOK)
|
}, http.StatusOK)
|
||||||
return
|
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
|
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
|
isAuthenticationRequired := false
|
||||||
if _, ok := data["response"].(map[string]interface{})["error"]; ok {
|
if _, ok := data["response"].(map[string]interface{})["error"]; ok {
|
||||||
isAuthenticationRequired = true
|
isAuthenticationRequired = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if isAuthenticationRequired && responseMode != constants.ResponseModeWebMessage {
|
if isAuthenticationRequired && responseMode != constants.ResponseModeWebMessage {
|
||||||
gc.Redirect(http.StatusFound, loginURI)
|
gc.Redirect(http.StatusFound, authURI)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user