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