fix: other auth recipes for oidc idp + remove logs
This commit is contained in:
@@ -45,9 +45,7 @@ import (
|
||||
"github.com/authorizerdev/authorizer/server/cookie"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/authorizerdev/authorizer/server/parsers"
|
||||
"github.com/authorizerdev/authorizer/server/token"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
)
|
||||
|
||||
// Check the flow for generating and verifying codes: https://developer.okta.com/blog/2019/08/22/okta-authjs-pkce#:~:text=PKCE%20works%20by%20having%20the,is%20called%20the%20Code%20Challenge.
|
||||
@@ -108,19 +106,11 @@ func AuthorizeHandler() gin.HandlerFunc {
|
||||
}
|
||||
|
||||
log := log.WithFields(log.Fields{
|
||||
"response_mode": responseMode,
|
||||
"response_type": responseType,
|
||||
"state": state,
|
||||
"code_challenge": codeChallenge,
|
||||
"scope": scope,
|
||||
"redirect_uri": redirectURI,
|
||||
"nonce": nonce,
|
||||
"code": code,
|
||||
"response_mode": responseMode,
|
||||
"response_type": responseType,
|
||||
})
|
||||
|
||||
// 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
|
||||
if responseType == constants.ResponseTypeCode {
|
||||
@@ -141,17 +131,6 @@ func AuthorizeHandler() gin.HandlerFunc {
|
||||
loginURL = "/app#" + loginState
|
||||
}
|
||||
|
||||
if state == "" {
|
||||
handleResponse(gc, responseMode, loginURL, redirectURI, map[string]interface{}{
|
||||
"type": "authorization_response",
|
||||
"response": map[string]interface{}{
|
||||
"error": "state_required",
|
||||
"error_description": "state is required",
|
||||
},
|
||||
}, http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
if responseType == constants.ResponseTypeCode && codeChallenge == "" {
|
||||
handleResponse(gc, responseMode, loginURL, redirectURI, map[string]interface{}{
|
||||
"type": "authorization_response",
|
||||
@@ -275,7 +254,6 @@ func AuthorizeHandler() gin.HandlerFunc {
|
||||
}
|
||||
|
||||
if responseType == constants.ResponseTypeToken || responseType == constants.ResponseTypeIDToken {
|
||||
hostname := parsers.GetHost(gc)
|
||||
// rollover the session for security
|
||||
authToken, err := token.CreateAuthToken(gc, user, claims.Roles, scope, claims.LoginMethod, nonce, "")
|
||||
if err != nil {
|
||||
@@ -299,7 +277,7 @@ func AuthorizeHandler() gin.HandlerFunc {
|
||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||
|
||||
// used of query mode
|
||||
params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(authToken.IDToken.ExpiresAt, 10) + "&state=" + state + "&id_token=" + authToken.IDToken.Token + "&code=" + code
|
||||
params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(authToken.IDToken.ExpiresAt, 10) + "&state=" + state + "&id_token=" + authToken.IDToken.Token
|
||||
|
||||
res := map[string]interface{}{
|
||||
"access_token": authToken.AccessToken.Token,
|
||||
@@ -308,19 +286,17 @@ func AuthorizeHandler() gin.HandlerFunc {
|
||||
"scope": scope,
|
||||
"token_type": "Bearer",
|
||||
"expires_in": authToken.AccessToken.ExpiresAt,
|
||||
"code": code,
|
||||
}
|
||||
|
||||
if utils.StringSliceContains(scope, "offline_access") {
|
||||
refreshToken, _, err := token.CreateRefreshToken(user, claims.Roles, scope, hostname, nonce, claims.LoginMethod)
|
||||
if err != nil {
|
||||
log.Debug("SetUserSession failed: ", err)
|
||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
||||
return
|
||||
}
|
||||
res["refresh_token"] = refreshToken
|
||||
params += "&refresh_token=" + refreshToken
|
||||
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+nonce, refreshToken)
|
||||
if nonce != "" {
|
||||
params += "&nonce=" + nonce
|
||||
res["nonce"] = nonce
|
||||
}
|
||||
|
||||
if authToken.RefreshToken != nil {
|
||||
res["refresh_token"] = authToken.RefreshToken.Token
|
||||
params += "&refresh_token=" + authToken.RefreshToken.Token
|
||||
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
|
||||
}
|
||||
|
||||
if responseMode == constants.ResponseModeQuery {
|
||||
@@ -349,6 +325,9 @@ func AuthorizeHandler() gin.HandlerFunc {
|
||||
}
|
||||
|
||||
func validateAuthorizeRequest(responseType, responseMode, clientID, state, codeChallenge string) error {
|
||||
if strings.TrimSpace(state) == "" {
|
||||
return fmt.Errorf("invalid state. state is required to prevent csrf attack", responseMode)
|
||||
}
|
||||
if responseType != constants.ResponseTypeCode && responseType != constants.ResponseTypeToken && responseType != constants.ResponseTypeIDToken {
|
||||
return fmt.Errorf("invalid response type %s. 'code' & 'token' are valid response_type", responseMode)
|
||||
}
|
||||
@@ -387,8 +366,6 @@ 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"],
|
||||
|
@@ -56,20 +56,20 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
||||
scopes := strings.Split(sessionSplit[3], ",")
|
||||
|
||||
user := models.User{}
|
||||
code := ctx.Request.FormValue("code")
|
||||
oauthCode := ctx.Request.FormValue("code")
|
||||
switch provider {
|
||||
case constants.AuthRecipeMethodGoogle:
|
||||
user, err = processGoogleUserInfo(code)
|
||||
user, err = processGoogleUserInfo(oauthCode)
|
||||
case constants.AuthRecipeMethodGithub:
|
||||
user, err = processGithubUserInfo(code)
|
||||
user, err = processGithubUserInfo(oauthCode)
|
||||
case constants.AuthRecipeMethodFacebook:
|
||||
user, err = processFacebookUserInfo(code)
|
||||
user, err = processFacebookUserInfo(oauthCode)
|
||||
case constants.AuthRecipeMethodLinkedIn:
|
||||
user, err = processLinkedInUserInfo(code)
|
||||
user, err = processLinkedInUserInfo(oauthCode)
|
||||
case constants.AuthRecipeMethodApple:
|
||||
user, err = processAppleUserInfo(code)
|
||||
user, err = processAppleUserInfo(oauthCode)
|
||||
case constants.AuthRecipeMethodTwitter:
|
||||
user, err = processTwitterUserInfo(code, sessionState)
|
||||
user, err = processTwitterUserInfo(oauthCode, sessionState)
|
||||
default:
|
||||
log.Info("Invalid oauth provider")
|
||||
err = fmt.Errorf(`invalid oauth provider`)
|
||||
@@ -200,19 +200,50 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
||||
// TODO
|
||||
// use stateValue to get code / nonce
|
||||
// add code / nonce to id_token
|
||||
nonce := uuid.New().String()
|
||||
authToken, err := token.CreateAuthToken(ctx, user, inputRoles, scopes, provider, nonce, "")
|
||||
code := ""
|
||||
codeChallenge := ""
|
||||
nonce := ""
|
||||
if stateValue != "" {
|
||||
// Get state from store
|
||||
authorizeState, _ := memorystore.Provider.GetState(stateValue)
|
||||
if authorizeState != "" {
|
||||
authorizeStateSplit := strings.Split(authorizeState, "@@")
|
||||
if len(authorizeStateSplit) > 1 {
|
||||
code = authorizeStateSplit[0]
|
||||
codeChallenge = authorizeStateSplit[1]
|
||||
} else {
|
||||
nonce = authorizeState
|
||||
}
|
||||
go memorystore.Provider.RemoveState(stateValue)
|
||||
}
|
||||
}
|
||||
if nonce == "" {
|
||||
nonce = uuid.New().String()
|
||||
}
|
||||
authToken, err := token.CreateAuthToken(ctx, user, inputRoles, scopes, provider, nonce, code)
|
||||
if err != nil {
|
||||
log.Debug("Failed to create auth token: ", err)
|
||||
ctx.JSON(500, gin.H{"error": err.Error()})
|
||||
}
|
||||
|
||||
// Code challenge could be optional if PKCE flow is not used
|
||||
if code != "" {
|
||||
if err := memorystore.Provider.SetState(code, codeChallenge+"@@"+authToken.FingerPrintHash); err != nil {
|
||||
log.Debug("SetState failed: ", err)
|
||||
ctx.JSON(500, gin.H{"error": err.Error()})
|
||||
}
|
||||
}
|
||||
|
||||
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
|
||||
if expiresIn <= 0 {
|
||||
expiresIn = 1
|
||||
}
|
||||
|
||||
params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(expiresIn, 10) + "&state=" + stateValue + "&id_token=" + authToken.IDToken.Token
|
||||
params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(expiresIn, 10) + "&state=" + stateValue + "&id_token=" + authToken.IDToken.Token + "&nonce=" + nonce
|
||||
|
||||
if code != "" {
|
||||
params += "&code=" + code
|
||||
}
|
||||
|
||||
sessionKey := provider + ":" + user.ID
|
||||
cookie.SetSession(ctx, authToken.FingerPrintHash)
|
||||
@@ -220,7 +251,7 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
||||
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
|
||||
|
||||
if authToken.RefreshToken != nil {
|
||||
params = params + `&refresh_token=` + authToken.RefreshToken.Token
|
||||
params += `&refresh_token=` + authToken.RefreshToken.Token
|
||||
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
|
||||
}
|
||||
|
||||
|
@@ -3,7 +3,6 @@ package handlers
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -33,10 +32,6 @@ type RequestBody struct {
|
||||
// grant type required
|
||||
func TokenHandler() gin.HandlerFunc {
|
||||
return func(gc *gin.Context) {
|
||||
// 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)
|
||||
@@ -47,8 +42,6 @@ func TokenHandler() gin.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("=>req body: %+v\n", reqBody)
|
||||
|
||||
codeVerifier := strings.TrimSpace(reqBody.CodeVerifier)
|
||||
code := strings.TrimSpace(reqBody.Code)
|
||||
clientID := strings.TrimSpace(reqBody.ClientID)
|
||||
@@ -125,7 +118,6 @@ func TokenHandler() gin.HandlerFunc {
|
||||
// [0] -> code_challenge
|
||||
// [1] -> session cookie
|
||||
sessionDataSplit := strings.Split(sessionData, "@@")
|
||||
fmt.Println("=> sessionDataSplit:", sessionDataSplit)
|
||||
|
||||
go memorystore.Provider.RemoveState(code)
|
||||
|
||||
@@ -135,7 +127,6 @@ func TokenHandler() gin.HandlerFunc {
|
||||
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",
|
||||
@@ -166,8 +157,6 @@ func TokenHandler() gin.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("=>claims: %+v\n", &claims)
|
||||
|
||||
userID = claims.Subject
|
||||
roles = claims.Roles
|
||||
scope = claims.Scope
|
||||
@@ -242,10 +231,6 @@ func TokenHandler() gin.HandlerFunc {
|
||||
}
|
||||
|
||||
nonce := uuid.New().String() + "@@" + code
|
||||
|
||||
fmt.Println("=> code", code)
|
||||
fmt.Println("=> nonce", nonce)
|
||||
|
||||
authToken, err := token.CreateAuthToken(gc, user, roles, scope, loginMethod, nonce, code)
|
||||
if err != nil {
|
||||
log.Debug("Error creating auth token: ", err)
|
||||
|
@@ -100,8 +100,29 @@ func VerifyEmailHandler() gin.HandlerFunc {
|
||||
loginMethod = constants.AuthRecipeMethodMagicLinkLogin
|
||||
}
|
||||
|
||||
nonce := uuid.New().String()
|
||||
authToken, err := token.CreateAuthToken(c, user, roles, scope, loginMethod, nonce, "")
|
||||
code := ""
|
||||
// Not required as /oauth/token cannot be resumed from other tab
|
||||
// codeChallenge := ""
|
||||
nonce := ""
|
||||
if state != "" {
|
||||
// Get state from store
|
||||
authorizeState, _ := memorystore.Provider.GetState(state)
|
||||
if authorizeState != "" {
|
||||
authorizeStateSplit := strings.Split(authorizeState, "@@")
|
||||
if len(authorizeStateSplit) > 1 {
|
||||
code = authorizeStateSplit[0]
|
||||
// Not required as /oauth/token cannot be resumed from other tab
|
||||
// codeChallenge = authorizeStateSplit[1]
|
||||
} else {
|
||||
nonce = authorizeState
|
||||
}
|
||||
go memorystore.Provider.RemoveState(state)
|
||||
}
|
||||
}
|
||||
if nonce == "" {
|
||||
nonce = uuid.New().String()
|
||||
}
|
||||
authToken, err := token.CreateAuthToken(c, user, roles, scope, loginMethod, nonce, code)
|
||||
if err != nil {
|
||||
log.Debug("Error creating auth token: ", err)
|
||||
errorRes["error_description"] = err.Error()
|
||||
@@ -109,12 +130,27 @@ func VerifyEmailHandler() gin.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
// Code challenge could be optional if PKCE flow is not used
|
||||
// Not required as /oauth/token cannot be resumed from other tab
|
||||
// if code != "" {
|
||||
// if err := memorystore.Provider.SetState(code, codeChallenge+"@@"+authToken.FingerPrintHash); err != nil {
|
||||
// log.Debug("Error setting code state ", err)
|
||||
// errorRes["error_description"] = err.Error()
|
||||
// c.JSON(500, errorRes)
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
||||
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
|
||||
if expiresIn <= 0 {
|
||||
expiresIn = 1
|
||||
}
|
||||
|
||||
params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(expiresIn, 10) + "&state=" + state + "&id_token=" + authToken.IDToken.Token
|
||||
params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(expiresIn, 10) + "&state=" + state + "&id_token=" + authToken.IDToken.Token + "&nonce=" + nonce
|
||||
|
||||
if code != "" {
|
||||
params += "&code=" + code
|
||||
}
|
||||
|
||||
sessionKey := loginMethod + ":" + user.ID
|
||||
cookie.SetSession(c, authToken.FingerPrintHash)
|
||||
|
Reference in New Issue
Block a user