fix: openid flow

This commit is contained in:
Lakhan Samani
2022-11-12 23:54:37 +05:30
parent 4775641431
commit 49556b1709
17 changed files with 1004 additions and 828 deletions

View File

@@ -86,10 +86,23 @@ func AuthorizeHandler() gin.HandlerFunc {
"code": code,
})
memorystore.Provider.SetState(codeChallenge, code)
// memorystore.Provider.SetState(codeChallenge, code)
// TODO add state with timeout
// used for response mode query or fragment
loginState := "state=" + state + "&scope=" + strings.Join(scope, " ") + "&redirect_uri=" + redirectURI + "&code=" + code + "&nonce=" + nonce
loginState := "state=" + state + "&scope=" + strings.Join(scope, " ") + "&redirect_uri=" + redirectURI
if responseType == constants.ResponseTypeCode {
loginState += "&code=" + code
if err := memorystore.Provider.SetState(state, code+"@@"+codeChallenge); err != nil {
log.Debug("Error setting temp code", err)
}
} else {
loginState += "&nonce=" + nonce
if err := memorystore.Provider.SetState(state, nonce); err != nil {
log.Debug("Error setting temp code", err)
}
}
loginURL := "/app?" + loginState
if responseMode == constants.ResponseModeFragment {
@@ -168,7 +181,15 @@ func AuthorizeHandler() gin.HandlerFunc {
return
}
if err := memorystore.Provider.SetState(codeChallenge, code+"@"+newSessionToken); err != nil {
// 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)
// 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)
return
@@ -317,13 +338,15 @@ func handleResponse(gc *gin.Context, responseMode, loginURI, redirectURI string,
isAuthenticationRequired = true
}
if isAuthenticationRequired {
gc.Redirect(http.StatusFound, loginURI)
return
}
switch responseMode {
case constants.ResponseModeQuery, constants.ResponseModeFragment:
if isAuthenticationRequired {
gc.Redirect(http.StatusFound, loginURI)
} else {
gc.Redirect(http.StatusFound, redirectURI)
}
gc.Redirect(http.StatusFound, redirectURI)
return
case constants.ResponseModeWebMessage:
gc.HTML(httpStatusCode, authorizeWebMessageTemplate, gin.H{
@@ -332,6 +355,8 @@ func handleResponse(gc *gin.Context, responseMode, loginURI, redirectURI string,
})
return
case constants.ResponseModeFormPost:
fmt.Println("=> trying tof orm post")
fmt.Printf("=> %+v \n", data["response"])
gc.HTML(httpStatusCode, authorizeFormPostTemplate, gin.H{
"target_origin": redirectURI,
"authorization_response": data["response"],

View File

@@ -3,6 +3,7 @@ package handlers
import (
"crypto/sha256"
"encoding/base64"
"fmt"
"net/http"
"strings"
"time"
@@ -18,12 +19,26 @@ import (
"github.com/authorizerdev/authorizer/server/token"
)
type RequestBody struct {
CodeVerifier string `form:"code_verifier" json:"code_verifier"`
Code string `form:"code" json:"code"`
ClientID string `form:"client_id" json:"client_id"`
ClientSecret string `form:"client_secret" json:"client_secret"`
GrantType string `form:"grant_type" json:"grant_type"`
RefreshToken string `form:"refresh_token" json:"refresh_token"`
RedirectURI string `form:"redirect_uri" json:"redirect_uri"`
}
// TokenHandler to handle /oauth/token requests
// grant type required
func TokenHandler() gin.HandlerFunc {
return func(gc *gin.Context) {
var reqBody map[string]string
if err := gc.BindJSON(&reqBody); err != nil {
// body := gc.Request.Body
// x, _ := ioutil.ReadAll(body)
// fmt.Printf("=> %s \n %s\n", string(x), gc.Request.Header.Get("Content-Type"))
var reqBody RequestBody
if err := gc.Bind(&reqBody); err != nil {
log.Debug("Error binding JSON: ", err)
gc.JSON(http.StatusBadRequest, gin.H{
"error": "error_binding_json",
@@ -32,11 +47,14 @@ func TokenHandler() gin.HandlerFunc {
return
}
codeVerifier := strings.TrimSpace(reqBody["code_verifier"])
code := strings.TrimSpace(reqBody["code"])
clientID := strings.TrimSpace(reqBody["client_id"])
grantType := strings.TrimSpace(reqBody["grant_type"])
refreshToken := strings.TrimSpace(reqBody["refresh_token"])
fmt.Printf("=>req body: %+v\n", reqBody)
codeVerifier := strings.TrimSpace(reqBody.CodeVerifier)
code := strings.TrimSpace(reqBody.Code)
clientID := strings.TrimSpace(reqBody.ClientID)
grantType := strings.TrimSpace(reqBody.GrantType)
refreshToken := strings.TrimSpace(reqBody.RefreshToken)
clientSecret := strings.TrimSpace(reqBody.ClientSecret)
if grantType == "" {
grantType = "authorization_code"
@@ -77,15 +95,6 @@ func TokenHandler() gin.HandlerFunc {
sessionKey := ""
if isAuthorizationCodeGrant {
if codeVerifier == "" {
log.Debug("Code verifier is empty")
gc.JSON(http.StatusBadRequest, gin.H{
"error": "invalid_code_verifier",
"error_description": "The code verifier is required",
})
return
}
if code == "" {
log.Debug("Code is empty")
gc.JSON(http.StatusBadRequest, gin.H{
@@ -95,33 +104,55 @@ func TokenHandler() gin.HandlerFunc {
return
}
hash := sha256.New()
hash.Write([]byte(codeVerifier))
encryptedCode := strings.ReplaceAll(base64.URLEncoding.EncodeToString(hash.Sum(nil)), "+", "-")
encryptedCode = strings.ReplaceAll(encryptedCode, "/", "_")
encryptedCode = strings.ReplaceAll(encryptedCode, "=", "")
sessionData, err := memorystore.Provider.GetState(encryptedCode)
if codeVerifier == "" && clientSecret == "" {
gc.JSON(http.StatusBadRequest, gin.H{
"error": "invalid_dat",
"error_description": "The code verifier or client secret is required",
})
return
}
// Get state
sessionData, err := memorystore.Provider.GetState(code)
if sessionData == "" || err != nil {
log.Debug("Session data is empty")
gc.JSON(http.StatusBadRequest, gin.H{
"error": "invalid_code_verifier",
"error_description": "The code verifier is invalid",
"error": "invalid_code",
"error_description": "The code is invalid",
})
return
}
go memorystore.Provider.RemoveState(encryptedCode)
// split session data
// it contains code@sessiontoken
sessionDataSplit := strings.Split(sessionData, "@")
// [0] -> code_challenge
// [1] -> session cookie
sessionDataSplit := strings.Split(sessionData, "@@")
fmt.Println("=> sessionDataSplit:", sessionDataSplit)
if sessionDataSplit[0] != code {
log.Debug("Invalid code verifier. Unable to split session data")
gc.JSON(http.StatusBadRequest, gin.H{
"error": "invalid_code_verifier",
"error_description": "The code verifier is invalid",
})
return
go memorystore.Provider.RemoveState(code)
if codeVerifier != "" {
hash := sha256.New()
hash.Write([]byte(codeVerifier))
encryptedCode := strings.ReplaceAll(base64.RawURLEncoding.EncodeToString(hash.Sum(nil)), "+", "-")
encryptedCode = strings.ReplaceAll(encryptedCode, "/", "_")
encryptedCode = strings.ReplaceAll(encryptedCode, "=", "")
fmt.Println("=> encryptedCode", encryptedCode)
if encryptedCode != sessionDataSplit[0] {
gc.JSON(http.StatusBadRequest, gin.H{
"error": "invalid_code_verifier",
"error_description": "The code verifier is invalid",
})
return
}
} else {
if clientHash, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyClientSecret); clientSecret != clientHash || err != nil {
log.Debug("Client Secret is invalid: ", clientID)
gc.JSON(http.StatusBadRequest, gin.H{
"error": "invalid_client_secret",
"error_description": "The client secret is invalid",
})
return
}
}
// validate session
@@ -135,6 +166,8 @@ func TokenHandler() gin.HandlerFunc {
return
}
fmt.Printf("=>claims: %+v\n", &claims)
userID = claims.Subject
roles = claims.Roles
scope = claims.Scope
@@ -147,6 +180,7 @@ func TokenHandler() gin.HandlerFunc {
}
go memorystore.Provider.DeleteUserSession(sessionKey, claims.Nonce)
} else {
// validate refresh token
if refreshToken == "" {
@@ -207,7 +241,11 @@ func TokenHandler() gin.HandlerFunc {
return
}
nonce := uuid.New().String()
nonce := uuid.New().String() + "@@" + code
fmt.Println("=> code", code)
fmt.Println("=> nonce", nonce)
authToken, err := token.CreateAuthToken(gc, user, roles, scope, loginMethod, nonce)
if err != nil {
log.Debug("Error creating auth token: ", err)