mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 10:41:03 +01:00
Complete the OAuth/OAuth2 'high level' support
This commit is contained in:
parent
56f78567a2
commit
4a446ac1e2
|
@ -104,6 +104,10 @@ type (
|
|||
// Mail contains the configs for the mail sender service
|
||||
Mail Mail
|
||||
|
||||
// OAuth the configs for the gothic oauth/oauth2 authentication for third-party websites
|
||||
// See https://github.com/iris-contrib/gothic/blob/master/example/main.go
|
||||
OAuth OAuth
|
||||
|
||||
// Server contains the configs for the http server
|
||||
// Server configs are the only one which are setted inside base Iris package (from Listen, ListenTLS, ListenUNIX) NO from users
|
||||
//
|
||||
|
@ -147,6 +151,7 @@ func Default() Iris {
|
|||
Render: DefaultRender(),
|
||||
Websocket: DefaultWebsocket(),
|
||||
Mail: DefaultMail(),
|
||||
OAuth: DefaultOAuth(),
|
||||
Server: DefaultServer(),
|
||||
}
|
||||
}
|
||||
|
|
213
config/oauth.go
Normal file
213
config/oauth.go
Normal file
|
@ -0,0 +1,213 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"github.com/imdario/mergo"
|
||||
"github.com/markbates/goth"
|
||||
"github.com/markbates/goth/providers/amazon"
|
||||
"github.com/markbates/goth/providers/bitbucket"
|
||||
"github.com/markbates/goth/providers/box"
|
||||
"github.com/markbates/goth/providers/digitalocean"
|
||||
"github.com/markbates/goth/providers/dropbox"
|
||||
"github.com/markbates/goth/providers/facebook"
|
||||
"github.com/markbates/goth/providers/github"
|
||||
"github.com/markbates/goth/providers/gitlab"
|
||||
"github.com/markbates/goth/providers/gplus"
|
||||
"github.com/markbates/goth/providers/heroku"
|
||||
"github.com/markbates/goth/providers/instagram"
|
||||
"github.com/markbates/goth/providers/lastfm"
|
||||
"github.com/markbates/goth/providers/linkedin"
|
||||
"github.com/markbates/goth/providers/onedrive"
|
||||
"github.com/markbates/goth/providers/paypal"
|
||||
"github.com/markbates/goth/providers/salesforce"
|
||||
"github.com/markbates/goth/providers/slack"
|
||||
"github.com/markbates/goth/providers/soundcloud"
|
||||
"github.com/markbates/goth/providers/spotify"
|
||||
"github.com/markbates/goth/providers/steam"
|
||||
"github.com/markbates/goth/providers/stripe"
|
||||
"github.com/markbates/goth/providers/twitch"
|
||||
"github.com/markbates/goth/providers/twitter"
|
||||
"github.com/markbates/goth/providers/uber"
|
||||
"github.com/markbates/goth/providers/wepay"
|
||||
"github.com/markbates/goth/providers/yahoo"
|
||||
"github.com/markbates/goth/providers/yammer"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultAuthPath /auth
|
||||
DefaultAuthPath = "/auth"
|
||||
)
|
||||
|
||||
// OAuth the configs for the gothic oauth/oauth2 authentication for third-party websites
|
||||
// All Key and Secret values are empty by default strings. Non-empty will be registered as Goth Provider automatically, by Iris
|
||||
// the users can still register their own providers using goth.UseProviders
|
||||
// contains the providers' keys (& secrets) and the relative auth callback url path(ex: "/auth" will be registered as /auth/:provider/callback)
|
||||
//
|
||||
type OAuth struct {
|
||||
Path string
|
||||
TwitterKey, TwitterSecret, TwitterName string
|
||||
FacebookKey, FacebookSecret, FacebookName string
|
||||
GplusKey, GplusSecret, GplusName string
|
||||
GithubKey, GithubSecret, GithubName string
|
||||
SpotifyKey, SpotifySecret, SpotifyName string
|
||||
LinkedinKey, LinkedinSecret, LinkedinName string
|
||||
LastfmKey, LastfmSecret, LastfmName string
|
||||
TwitchKey, TwitchSecret, TwitchName string
|
||||
DropboxKey, DropboxSecret, DropboxName string
|
||||
DigitaloceanKey, DigitaloceanSecret, DigitaloceanName string
|
||||
BitbucketKey, BitbucketSecret, BitbucketName string
|
||||
InstagramKey, InstagramSecret, InstagramName string
|
||||
BoxKey, BoxSecret, BoxName string
|
||||
SalesforceKey, SalesforceSecret, SalesforceName string
|
||||
AmazonKey, AmazonSecret, AmazonName string
|
||||
YammerKey, YammerSecret, YammerName string
|
||||
OneDriveKey, OneDriveSecret, OneDriveName string
|
||||
YahooKey, YahooSecret, YahooName string
|
||||
SlackKey, SlackSecret, SlackName string
|
||||
StripeKey, StripeSecret, StripeName string
|
||||
WepayKey, WepaySecret, WepayName string
|
||||
PaypalKey, PaypalSecret, PaypalName string
|
||||
SteamKey, SteamName string
|
||||
HerokuKey, HerokuSecret, HerokuName string
|
||||
UberKey, UberSecret, UberName string
|
||||
SoundcloudKey, SoundcloudSecret, SoundcloudName string
|
||||
GitlabKey, GitlabSecret, GitlabName string
|
||||
}
|
||||
|
||||
// DefaultOAuth returns OAuth config, the fields of the iteral are zero-values ( empty strings)
|
||||
func DefaultOAuth() OAuth {
|
||||
return OAuth{
|
||||
Path: DefaultAuthPath,
|
||||
TwitterName: "twitter",
|
||||
FacebookName: "facebook",
|
||||
GplusName: "gplus",
|
||||
GithubName: "github",
|
||||
SpotifyName: "spotify",
|
||||
LinkedinName: "linkedin",
|
||||
LastfmName: "lastfm",
|
||||
TwitchName: "twitch",
|
||||
DropboxName: "dropbox",
|
||||
DigitaloceanName: "digitalocean",
|
||||
BitbucketName: "bitbucket",
|
||||
InstagramName: "instagram",
|
||||
BoxName: "box",
|
||||
SalesforceName: "salesforce",
|
||||
AmazonName: "amazon",
|
||||
YammerName: "yammer",
|
||||
OneDriveName: "onedrive",
|
||||
YahooName: "yahoo",
|
||||
SlackName: "slack",
|
||||
StripeName: "stripe",
|
||||
WepayName: "wepay",
|
||||
PaypalName: "paypal",
|
||||
SteamName: "steam",
|
||||
HerokuName: "heroku",
|
||||
UberName: "uber",
|
||||
SoundcloudName: "soundcloud",
|
||||
GitlabName: "gitlab",
|
||||
} // this will be registered as /auth/:provider in the mux
|
||||
}
|
||||
|
||||
// MergeSingle merges the default with the given config and returns the result
|
||||
func (c OAuth) MergeSingle(cfg OAuth) (config OAuth) {
|
||||
|
||||
config = cfg
|
||||
mergo.Merge(&config, c)
|
||||
return
|
||||
}
|
||||
|
||||
// GetAll returns the valid goth providers and the relative url paths (because the goth.Provider doesn't have a public method to get the Auth path...)
|
||||
// we do the hard-core/hand checking here at the configs.
|
||||
//
|
||||
// receives one parameter which is the host from the server,ex: http://localhost:3000, will be used as prefix for the oauth callback
|
||||
func (c OAuth) GetAll(vhost string) (providers []goth.Provider) {
|
||||
|
||||
getCallbackURL := func(providerName string) string {
|
||||
return vhost + c.Path + "/" + providerName + "/callback"
|
||||
}
|
||||
|
||||
//we could use a map but that's easier for the users because of code completion of their IDEs/editors
|
||||
if c.TwitterKey != "" && c.TwitterSecret != "" {
|
||||
println(getCallbackURL("twitter"))
|
||||
providers = append(providers, twitter.New(c.TwitterKey, c.TwitterSecret, getCallbackURL(c.TwitterName)))
|
||||
}
|
||||
if c.FacebookKey != "" && c.FacebookSecret != "" {
|
||||
providers = append(providers, facebook.New(c.FacebookKey, c.FacebookSecret, getCallbackURL(c.FacebookName)))
|
||||
}
|
||||
if c.GplusKey != "" && c.GplusSecret != "" {
|
||||
providers = append(providers, gplus.New(c.GplusKey, c.GplusSecret, getCallbackURL(c.GplusName)))
|
||||
}
|
||||
if c.GithubKey != "" && c.GithubSecret != "" {
|
||||
providers = append(providers, github.New(c.GithubKey, c.GithubSecret, getCallbackURL(c.GithubName)))
|
||||
}
|
||||
if c.SpotifyKey != "" && c.SpotifySecret != "" {
|
||||
providers = append(providers, spotify.New(c.SpotifyKey, c.SpotifySecret, getCallbackURL(c.SpotifyName)))
|
||||
}
|
||||
if c.LinkedinKey != "" && c.LinkedinSecret != "" {
|
||||
providers = append(providers, linkedin.New(c.LinkedinKey, c.LinkedinSecret, getCallbackURL(c.LinkedinName)))
|
||||
}
|
||||
if c.LastfmKey != "" && c.LastfmSecret != "" {
|
||||
providers = append(providers, lastfm.New(c.LastfmKey, c.LastfmSecret, getCallbackURL(c.LastfmName)))
|
||||
}
|
||||
if c.TwitchKey != "" && c.TwitchSecret != "" {
|
||||
providers = append(providers, twitch.New(c.TwitchKey, c.TwitchSecret, getCallbackURL(c.TwitchName)))
|
||||
}
|
||||
if c.DropboxKey != "" && c.DropboxSecret != "" {
|
||||
providers = append(providers, dropbox.New(c.DropboxKey, c.DropboxSecret, getCallbackURL(c.DropboxName)))
|
||||
}
|
||||
if c.DigitaloceanKey != "" && c.DigitaloceanSecret != "" {
|
||||
providers = append(providers, digitalocean.New(c.DigitaloceanKey, c.DigitaloceanSecret, getCallbackURL(c.DigitaloceanName)))
|
||||
}
|
||||
if c.BitbucketKey != "" && c.BitbucketSecret != "" {
|
||||
providers = append(providers, bitbucket.New(c.BitbucketKey, c.BitbucketSecret, getCallbackURL(c.BitbucketName)))
|
||||
}
|
||||
if c.InstagramKey != "" && c.InstagramSecret != "" {
|
||||
providers = append(providers, instagram.New(c.InstagramKey, c.InstagramSecret, getCallbackURL(c.InstagramName)))
|
||||
}
|
||||
if c.BoxKey != "" && c.BoxSecret != "" {
|
||||
providers = append(providers, box.New(c.BoxKey, c.BoxSecret, getCallbackURL(c.BoxName)))
|
||||
}
|
||||
if c.SalesforceKey != "" && c.SalesforceSecret != "" {
|
||||
providers = append(providers, salesforce.New(c.SalesforceKey, c.SalesforceSecret, getCallbackURL(c.SalesforceName)))
|
||||
}
|
||||
if c.AmazonKey != "" && c.AmazonSecret != "" {
|
||||
providers = append(providers, amazon.New(c.AmazonKey, c.AmazonSecret, getCallbackURL(c.AmazonName)))
|
||||
}
|
||||
if c.YammerKey != "" && c.YammerSecret != "" {
|
||||
providers = append(providers, yammer.New(c.YammerKey, c.YammerSecret, getCallbackURL(c.YammerName)))
|
||||
}
|
||||
if c.OneDriveKey != "" && c.OneDriveSecret != "" {
|
||||
providers = append(providers, onedrive.New(c.OneDriveKey, c.OneDriveSecret, getCallbackURL(c.OneDriveName)))
|
||||
}
|
||||
if c.YahooKey != "" && c.YahooSecret != "" {
|
||||
providers = append(providers, yahoo.New(c.YahooKey, c.YahooSecret, getCallbackURL(c.YahooName)))
|
||||
}
|
||||
if c.SlackKey != "" && c.SlackSecret != "" {
|
||||
providers = append(providers, slack.New(c.SlackKey, c.SlackSecret, getCallbackURL(c.SlackName)))
|
||||
}
|
||||
if c.StripeKey != "" && c.StripeSecret != "" {
|
||||
providers = append(providers, stripe.New(c.StripeKey, c.StripeSecret, getCallbackURL(c.StripeName)))
|
||||
}
|
||||
if c.WepayKey != "" && c.WepaySecret != "" {
|
||||
providers = append(providers, wepay.New(c.WepayKey, c.WepaySecret, getCallbackURL(c.WepayName)))
|
||||
}
|
||||
if c.PaypalKey != "" && c.PaypalSecret != "" {
|
||||
providers = append(providers, paypal.New(c.PaypalKey, c.PaypalSecret, getCallbackURL(c.PaypalName)))
|
||||
}
|
||||
if c.SteamKey != "" {
|
||||
providers = append(providers, steam.New(c.SteamKey, getCallbackURL(c.SteamName)))
|
||||
}
|
||||
if c.HerokuKey != "" && c.HerokuSecret != "" {
|
||||
providers = append(providers, heroku.New(c.HerokuKey, c.HerokuSecret, getCallbackURL(c.HerokuName)))
|
||||
}
|
||||
if c.UberKey != "" && c.UberSecret != "" {
|
||||
providers = append(providers, uber.New(c.UberKey, c.UberSecret, getCallbackURL(c.UberName)))
|
||||
}
|
||||
if c.SoundcloudKey != "" && c.SoundcloudSecret != "" {
|
||||
providers = append(providers, soundcloud.New(c.SoundcloudKey, c.SoundcloudSecret, getCallbackURL(c.SoundcloudName)))
|
||||
}
|
||||
if c.GitlabKey != "" && c.GitlabSecret != "" {
|
||||
providers = append(providers, gitlab.New(c.GitlabKey, c.GitlabSecret, getCallbackURL(c.GithubName)))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
23
context.go
23
context.go
|
@ -24,7 +24,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/iris-contrib/formBinder"
|
||||
"github.com/iris-contrib/gothic"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/errors"
|
||||
|
@ -88,6 +87,8 @@ type (
|
|||
sessionStore store.IStore
|
||||
// pos is the position number of the Context, look .Next to understand
|
||||
pos uint8
|
||||
//gothic oauth
|
||||
oauthUser goth.User
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -786,13 +787,15 @@ func (ctx *Context) Log(format string, a ...interface{}) {
|
|||
|
||||
/* Auth */
|
||||
|
||||
// CompleteUserAuth does what it says on the tin. It completes the authentication
|
||||
// process and fetches all of the basic information about the user from the provider.
|
||||
//
|
||||
// It expects to be able to get the name of the provider from the named parameters
|
||||
// as either "provider" or url query parameter ":provider".
|
||||
//
|
||||
// See https://github.com/iris-contrib/gothic/blob/master/example/main.go to see this in action.
|
||||
func (ctx *Context) CompleteUserAuth() (goth.User, error) {
|
||||
return gothic.CompleteUserAuth(ctx)
|
||||
// SetOAuthUser sets the oauth user
|
||||
// Internal method but exported because useful for advanced use cases
|
||||
// Iris uses this method to set automatically the authenticated user.
|
||||
func (ctx *Context) SetOAuthUser(u goth.User) {
|
||||
ctx.oauthUser = u
|
||||
}
|
||||
|
||||
// OAuthUser returns the oauthenticated User
|
||||
// See https://github.com/iris-contrib/gothic/blob/master/exampl/main.go to see this in action.
|
||||
func (ctx *Context) OAuthUser() goth.User {
|
||||
return ctx.oauthUser
|
||||
}
|
||||
|
|
|
@ -136,8 +136,12 @@ type (
|
|||
|
||||
// IContextAuth handles the authentication/authorization
|
||||
IContextAuth interface {
|
||||
// CompleteUserAuth
|
||||
// SetOAuthUser sets the oauth user
|
||||
// Internal method but exported because useful for advanced use cases
|
||||
// Iris uses this method to set automatically the authenticated user.
|
||||
SetOAuthUser(goth.User)
|
||||
// OAuthUser returns the authenticated User
|
||||
// See https://github.com/iris-contrib/gothic/blob/master/example/main.go to see this in action.
|
||||
CompleteUserAuth() (goth.User, error)
|
||||
OAuthUser() goth.User
|
||||
}
|
||||
)
|
||||
|
|
17
http.go
17
http.go
|
@ -286,9 +286,26 @@ func (s *Server) Host() (host string) {
|
|||
// VirtualHost returns the s.Config.ListeningAddr
|
||||
//
|
||||
func (s *Server) VirtualHost() (host string) {
|
||||
// check the addr if :8080 do it 0.0.0.0:8080 ,we need the hostname for many cases
|
||||
a := s.Config.ListeningAddr
|
||||
//check if contains hostname, we need the full host, :8080 should be : 127.0.0.1:8080
|
||||
if portIdx := strings.IndexByte(a, ':'); portIdx == 0 {
|
||||
// then the : is the first letter, so we dont have setted a hostname, lets set it
|
||||
s.Config.ListeningAddr = config.DefaultServerHostname + a
|
||||
}
|
||||
return s.Config.ListeningAddr
|
||||
}
|
||||
|
||||
// Fullhost returns the scheme+host
|
||||
func (s *Server) FullHost() string {
|
||||
scheme := "http://"
|
||||
// we need to be able to take that before(for testing &debugging) and after server's listen
|
||||
if s.IsSecure() || (s.Config.CertFile != "" && s.Config.KeyFile != "") {
|
||||
scheme = "https://"
|
||||
}
|
||||
return scheme + s.VirtualHost()
|
||||
}
|
||||
|
||||
// Hostname returns the hostname part only, if host == localhost:8080 it will return the localhost
|
||||
// if server is not listening it returns the config.ListeningAddr's hostname part
|
||||
func (s *Server) Hostname() (hostname string) {
|
||||
|
|
|
@ -6,9 +6,11 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/iris-contrib/gothic"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/logger"
|
||||
"github.com/kataras/iris/websocket"
|
||||
"github.com/markbates/goth"
|
||||
|
||||
"github.com/kataras/iris/mail"
|
||||
"github.com/kataras/iris/render/rest"
|
||||
|
@ -68,10 +70,11 @@ const (
|
|||
// Implements the FrameworkAPI
|
||||
type Framework struct {
|
||||
*muxAPI
|
||||
rest *rest.Render
|
||||
templates *template.Template
|
||||
sessions *sessions.Manager
|
||||
mailer mail.Service
|
||||
rest *rest.Render
|
||||
templates *template.Template
|
||||
sessions *sessions.Manager
|
||||
mailer mail.Service
|
||||
oauthHandlers Middleware
|
||||
// fields which are useful to the user/dev
|
||||
HTTPServer *Server
|
||||
Config *config.Iris
|
||||
|
@ -115,13 +118,48 @@ func (s *Framework) initialize() {
|
|||
s.sessions = sessions.New(s.Config.Sessions)
|
||||
}
|
||||
|
||||
//set the rest
|
||||
// set the rest
|
||||
s.rest = rest.New(s.Config.Render.Rest)
|
||||
|
||||
//set mail and templates if not already setted
|
||||
// set mail and templates if not already setted
|
||||
s.prepareMailer()
|
||||
s.prepareTemplates()
|
||||
|
||||
// set the oauth providers from the OAuth configuration field
|
||||
|
||||
// the user still can set his/her own provider (using goth.UseProviders), if the configuration for the provider is not exists
|
||||
// prepare the configs
|
||||
s.Config.OAuth = config.DefaultOAuth().MergeSingle(s.Config.OAuth)
|
||||
oauthProviders := s.Config.OAuth.GetAll(s.HTTPServer.FullHost())
|
||||
if len(oauthProviders) > 0 {
|
||||
goth.UseProviders(oauthProviders...)
|
||||
// set the mux path to handle these providers
|
||||
s.Get(s.Config.OAuth.Path+"/:provider", func(ctx *Context) {
|
||||
err := gothic.BeginAuthHandler(ctx)
|
||||
if err != nil {
|
||||
s.Logger.Warningf("\n[IRIS: OAUTH] Error:" + err.Error())
|
||||
}
|
||||
})
|
||||
|
||||
authMiddleware := func(ctx *Context) {
|
||||
|
||||
user, err := gothic.CompleteUserAuth(ctx)
|
||||
if err != nil {
|
||||
ctx.EmitError(StatusUnauthorized)
|
||||
ctx.Log(err.Error())
|
||||
return
|
||||
}
|
||||
ctx.SetOAuthUser(user)
|
||||
ctx.Next()
|
||||
}
|
||||
|
||||
s.oauthHandlers = append([]Handler{HandlerFunc(authMiddleware)}, s.oauthHandlers...)
|
||||
|
||||
s.Handle(MethodGet, s.Config.OAuth.Path+"/:provider/callback", s.oauthHandlers...)("oauth")
|
||||
}
|
||||
|
||||
// end of auth
|
||||
|
||||
// listen to websocket connections
|
||||
websocket.RegisterServer(s, s.Websocket, s.Logger)
|
||||
|
||||
|
|
24
iris.go
24
iris.go
|
@ -59,7 +59,6 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/iris-contrib/gothic"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/errors"
|
||||
|
@ -97,6 +96,7 @@ type (
|
|||
MustUseFunc(...HandlerFunc)
|
||||
OnError(int, HandlerFunc)
|
||||
EmitError(int, *Context)
|
||||
OnUserOAuth(...HandlerFunc)
|
||||
Lookup(string) Route
|
||||
Lookups() []Route
|
||||
Path(string, ...interface{}) string
|
||||
|
@ -568,16 +568,18 @@ func (s *Framework) TemplateString(templateFile string, pageContext interface{},
|
|||
return res
|
||||
}
|
||||
|
||||
// BeginAuthHandler is a convienence handler for starting the authentication process.
|
||||
// It expects to be able to get the name of the provider from the named parameters
|
||||
// as either "provider" or url query parameter ":provider".
|
||||
//
|
||||
// BeginAuthHandler will redirect the user to the appropriate authentication end-point
|
||||
// for the requested provider.
|
||||
//
|
||||
// See https://github.com/iris-contrib/gothic/blob/master/example/main.go to see this in action.
|
||||
func BeginAuthHandler(ctx *Context) {
|
||||
gothic.BeginAuthHandler(ctx)
|
||||
/* Auth */
|
||||
|
||||
// OnUserOAuth fires the middleware when the user logged in successfully via gothic oauth
|
||||
// get the user using the context.OAuthUser()
|
||||
func OnUserOAuth(handlersFn ...HandlerFunc) {
|
||||
Default.OnUserOAuth(handlersFn...)
|
||||
}
|
||||
|
||||
// OnUserOAuth fires the middleware when the user logged in successfully via gothic oauth
|
||||
// get the user using the context.OAuthUser()
|
||||
func (s *Framework) OnUserOAuth(handlersFn ...HandlerFunc) {
|
||||
s.oauthHandlers = append(s.oauthHandlers, convertToHandlers(handlersFn)...)
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
|
|
|
@ -31,6 +31,7 @@ func New(c config.Logger) *Logger {
|
|||
color.Output = colorable.NewColorable(c.Out)
|
||||
|
||||
l := &Logger{&c, color.New(attr(c.ColorBgDefault), attr(c.ColorFgDefault), color.Bold)}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user