Files
authorizer/server/handlers/oauth_login.go

244 lines
8.3 KiB
Go
Raw Normal View History

package handlers
import (
"net/http"
"strings"
2022-05-23 11:52:51 +05:30
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
2022-06-14 12:06:46 +05:30
"golang.org/x/oauth2"
2022-05-23 11:52:51 +05:30
"github.com/authorizerdev/authorizer/server/constants"
2022-05-27 23:20:38 +05:30
"github.com/authorizerdev/authorizer/server/memorystore"
2021-07-23 21:57:44 +05:30
"github.com/authorizerdev/authorizer/server/oauth"
2022-05-30 11:54:16 +05:30
"github.com/authorizerdev/authorizer/server/parsers"
2022-08-14 20:19:48 +02:00
"github.com/authorizerdev/authorizer/server/utils"
2022-05-30 11:54:16 +05:30
"github.com/authorizerdev/authorizer/server/validators"
)
2022-01-17 11:32:13 +05:30
// OAuthLoginHandler set host in the oauth state that is useful for redirecting to oauth_callback
func OAuthLoginHandler() gin.HandlerFunc {
return func(c *gin.Context) {
2022-05-30 11:54:16 +05:30
hostname := parsers.GetHost(c)
2022-03-16 21:44:57 +05:30
// deprecating redirectURL instead use redirect_uri
redirectURI := strings.TrimSpace(c.Query("redirectURL"))
2022-03-16 21:44:57 +05:30
if redirectURI == "" {
redirectURI = strings.TrimSpace(c.Query("redirect_uri"))
}
roles := strings.TrimSpace(c.Query("roles"))
state := strings.TrimSpace(c.Query("state"))
scopeString := strings.TrimSpace(c.Query("scope"))
if redirectURI == "" {
2022-05-23 11:52:51 +05:30
log.Debug("redirect_uri is empty")
c.JSON(400, gin.H{
"error": "invalid redirect uri",
})
return
}
if state == "" {
2022-05-23 11:52:51 +05:30
log.Debug("state is empty")
c.JSON(400, gin.H{
"error": "invalid state",
})
return
}
var scope []string
if scopeString == "" {
scope = []string{"openid", "profile", "email"}
} else {
scope = strings.Split(scopeString, " ")
}
if roles != "" {
// validate role
rolesSplit := strings.Split(roles, ",")
// use protected roles verification for admin login only.
// though if not associated with user, it will be rejected from oauth_callback
2022-05-31 08:14:03 +05:30
rolesString, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyRoles)
roles := []string{}
2022-05-29 17:22:46 +05:30
if err != nil {
log.Debug("Error getting roles: ", err)
2022-05-31 08:14:03 +05:30
rolesString = ""
} else {
roles = strings.Split(rolesString, ",")
2022-05-29 17:22:46 +05:30
}
2022-05-31 08:14:03 +05:30
protectedRolesString, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyProtectedRoles)
protectedRoles := []string{}
2022-05-29 17:22:46 +05:30
if err != nil {
log.Debug("Error getting protected roles: ", err)
2022-05-31 08:14:03 +05:30
protectedRolesString = ""
} else {
protectedRoles = strings.Split(protectedRolesString, ",")
2022-05-29 17:22:46 +05:30
}
2022-05-30 11:54:16 +05:30
if !validators.IsValidRoles(rolesSplit, append([]string{}, append(roles, protectedRoles...)...)) {
2022-05-25 12:30:22 +05:30
log.Debug("Invalid roles: ", roles)
c.JSON(400, gin.H{
"error": "invalid role",
})
return
}
} else {
2022-05-31 08:14:03 +05:30
defaultRoles, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyDefaultRoles)
2022-05-29 17:22:46 +05:30
if err != nil {
log.Debug("Error getting default roles: ", err)
c.JSON(400, gin.H{
"error": "invalid role",
})
return
}
2022-05-31 08:14:03 +05:30
roles = defaultRoles
2022-05-29 17:22:46 +05:30
}
oauthStateString := state + "___" + redirectURI + "___" + roles + "___" + strings.Join(scope, " ")
provider := c.Param("oauth_provider")
2021-12-03 22:55:27 +05:30
isProviderConfigured := true
switch provider {
case constants.AuthRecipeMethodGoogle:
2021-12-03 22:55:27 +05:30
if oauth.OAuthProviders.GoogleConfig == nil {
2022-05-23 11:52:51 +05:30
log.Debug("Google OAuth provider is not configured")
2021-12-03 22:55:27 +05:30
isProviderConfigured = false
break
}
err := memorystore.Provider.SetState(oauthStateString, constants.AuthRecipeMethodGoogle)
2022-05-29 17:22:46 +05:30
if err != nil {
log.Debug("Error setting state: ", err)
c.JSON(500, gin.H{
"error": "internal server error",
})
return
}
// during the init of OAuthProvider authorizer url might be empty
oauth.OAuthProviders.GoogleConfig.RedirectURL = hostname + "/oauth_callback/" + constants.AuthRecipeMethodGoogle
2021-12-03 22:55:27 +05:30
url := oauth.OAuthProviders.GoogleConfig.AuthCodeURL(oauthStateString)
c.Redirect(http.StatusTemporaryRedirect, url)
case constants.AuthRecipeMethodGithub:
2021-12-03 22:55:27 +05:30
if oauth.OAuthProviders.GithubConfig == nil {
2022-05-23 11:52:51 +05:30
log.Debug("Github OAuth provider is not configured")
2021-12-03 22:55:27 +05:30
isProviderConfigured = false
break
}
err := memorystore.Provider.SetState(oauthStateString, constants.AuthRecipeMethodGithub)
2022-05-29 17:22:46 +05:30
if err != nil {
log.Debug("Error setting state: ", err)
c.JSON(500, gin.H{
"error": "internal server error",
})
return
}
oauth.OAuthProviders.GithubConfig.RedirectURL = hostname + "/oauth_callback/" + constants.AuthRecipeMethodGithub
2021-12-03 22:55:27 +05:30
url := oauth.OAuthProviders.GithubConfig.AuthCodeURL(oauthStateString)
2021-07-18 04:48:42 +05:30
c.Redirect(http.StatusTemporaryRedirect, url)
case constants.AuthRecipeMethodFacebook:
2021-12-03 22:55:27 +05:30
if oauth.OAuthProviders.FacebookConfig == nil {
2022-05-23 11:52:51 +05:30
log.Debug("Facebook OAuth provider is not configured")
2021-12-03 22:55:27 +05:30
isProviderConfigured = false
break
}
err := memorystore.Provider.SetState(oauthStateString, constants.AuthRecipeMethodFacebook)
2022-05-29 17:22:46 +05:30
if err != nil {
log.Debug("Error setting state: ", err)
c.JSON(500, gin.H{
"error": "internal server error",
})
return
}
oauth.OAuthProviders.FacebookConfig.RedirectURL = hostname + "/oauth_callback/" + constants.AuthRecipeMethodFacebook
2021-12-03 22:55:27 +05:30
url := oauth.OAuthProviders.FacebookConfig.AuthCodeURL(oauthStateString)
2021-09-05 03:57:29 +05:30
c.Redirect(http.StatusTemporaryRedirect, url)
case constants.AuthRecipeMethodLinkedIn:
2022-06-06 22:08:32 +05:30
if oauth.OAuthProviders.LinkedInConfig == nil {
log.Debug("Linkedin OAuth provider is not configured")
isProviderConfigured = false
break
}
err := memorystore.Provider.SetState(oauthStateString, constants.AuthRecipeMethodLinkedIn)
2022-06-06 22:08:32 +05:30
if err != nil {
log.Debug("Error setting state: ", err)
c.JSON(500, gin.H{
"error": "internal server error",
})
return
}
oauth.OAuthProviders.LinkedInConfig.RedirectURL = hostname + "/oauth_callback/" + constants.AuthRecipeMethodLinkedIn
2022-06-06 22:08:32 +05:30
url := oauth.OAuthProviders.LinkedInConfig.AuthCodeURL(oauthStateString)
c.Redirect(http.StatusTemporaryRedirect, url)
2022-08-13 12:35:00 +05:30
case constants.AuthRecipeMethodTwitter:
if oauth.OAuthProviders.TwitterConfig == nil {
log.Debug("Twitter OAuth provider is not configured")
isProviderConfigured = false
break
}
2022-08-14 20:19:48 +02:00
verifier, challenge := utils.GenerateCodeChallenge()
err := memorystore.Provider.SetState(oauthStateString, verifier)
2022-08-13 12:35:00 +05:30
if err != nil {
log.Debug("Error setting state: ", err)
c.JSON(500, gin.H{
"error": "internal server error",
})
return
}
oauth.OAuthProviders.TwitterConfig.RedirectURL = hostname + "/oauth_callback/" + constants.AuthRecipeMethodTwitter
2022-08-14 20:19:48 +02:00
url := oauth.OAuthProviders.TwitterConfig.AuthCodeURL(oauthStateString, oauth2.SetAuthURLParam("code_challenge", challenge), oauth2.SetAuthURLParam("code_challenge_method", "S256"))
2022-08-13 12:35:00 +05:30
c.Redirect(http.StatusTemporaryRedirect, url)
case constants.AuthRecipeMethodApple:
if oauth.OAuthProviders.AppleConfig == nil {
2022-06-14 12:06:46 +05:30
log.Debug("Apple OAuth provider is not configured")
isProviderConfigured = false
break
}
err := memorystore.Provider.SetState(oauthStateString, constants.AuthRecipeMethodApple)
if err != nil {
log.Debug("Error setting state: ", err)
c.JSON(500, gin.H{
"error": "internal server error",
})
return
}
oauth.OAuthProviders.AppleConfig.RedirectURL = hostname + "/oauth_callback/" + constants.AuthRecipeMethodApple
2022-06-14 15:47:08 +05:30
// there is scope encoding issue with oauth2 and how apple expects, hence added scope manually
// check: https://github.com/golang/oauth2/issues/449
url := oauth.OAuthProviders.AppleConfig.AuthCodeURL(oauthStateString, oauth2.SetAuthURLParam("response_mode", "form_post")) + "&scope=name email"
c.Redirect(http.StatusTemporaryRedirect, url)
2023-02-26 05:23:02 +05:30
case constants.AuthRecipeMethodMicrosoft:
if oauth.OAuthProviders.MicrosoftConfig == nil {
log.Debug("Microsoft OAuth provider is not configured")
isProviderConfigured = false
break
}
err := memorystore.Provider.SetState(oauthStateString, constants.AuthRecipeMethodMicrosoft)
if err != nil {
log.Debug("Error setting state: ", err)
c.JSON(500, gin.H{
"error": "internal server error",
})
return
}
// during the init of OAuthProvider authorizer url might be empty
oauth.OAuthProviders.MicrosoftConfig.RedirectURL = hostname + "/oauth_callback/" + constants.AuthRecipeMethodMicrosoft
url := oauth.OAuthProviders.MicrosoftConfig.AuthCodeURL(oauthStateString)
c.Redirect(http.StatusTemporaryRedirect, url)
default:
2022-05-23 11:52:51 +05:30
log.Debug("Invalid oauth provider: ", provider)
c.JSON(422, gin.H{
"message": "Invalid oauth provider",
})
2021-07-18 04:48:42 +05:30
}
2021-12-03 22:55:27 +05:30
if !isProviderConfigured {
c.JSON(422, gin.H{
"message": provider + " not configured",
})
}
}
}