mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 18:51:03 +01:00
ed45c77be5
Former-commit-id: ed635ee95de7160cde11eaabc0c1dcb0e460a620
408 lines
15 KiB
Go
408 lines
15 KiB
Go
package main
|
|
|
|
// Any OAuth2 (even the pure golang/x/net/oauth2) package
|
|
// can be used with iris but at this example we will see the markbates' goth:
|
|
//
|
|
// $ go get github.com/markbates/goth/...
|
|
//
|
|
// This OAuth2 example works with sessions, so we will need
|
|
// to attach a session manager.
|
|
// Optionally: for even more secure session values,
|
|
// developers can use any third-party package to add a custom cookie encoder/decoder.
|
|
// At this example we will use the gorilla's securecookie:
|
|
//
|
|
// $ go get github.com/gorilla/securecookie
|
|
// Example of securecookie can be found at "sessions/securecookie" example folder.
|
|
|
|
// Notes:
|
|
// The whole example is converted by markbates/goth/example/main.go.
|
|
// It's tested with my own TWITTER application and it worked, even for localhost.
|
|
// I guess that everything else works as expected, all bugs reported by goth library's community
|
|
// are fixed in the time I wrote that example, have fun!
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"sort"
|
|
|
|
"github.com/kataras/iris/v12"
|
|
|
|
"github.com/kataras/iris/v12/sessions"
|
|
|
|
"github.com/gorilla/securecookie" // optionally, used for session's encoder/decoder
|
|
|
|
"github.com/markbates/goth"
|
|
"github.com/markbates/goth/providers/amazon"
|
|
"github.com/markbates/goth/providers/auth0"
|
|
"github.com/markbates/goth/providers/bitbucket"
|
|
"github.com/markbates/goth/providers/box"
|
|
"github.com/markbates/goth/providers/dailymotion"
|
|
"github.com/markbates/goth/providers/deezer"
|
|
"github.com/markbates/goth/providers/digitalocean"
|
|
"github.com/markbates/goth/providers/discord"
|
|
"github.com/markbates/goth/providers/dropbox"
|
|
"github.com/markbates/goth/providers/facebook"
|
|
"github.com/markbates/goth/providers/fitbit"
|
|
"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/intercom"
|
|
"github.com/markbates/goth/providers/lastfm"
|
|
"github.com/markbates/goth/providers/linkedin"
|
|
"github.com/markbates/goth/providers/meetup"
|
|
"github.com/markbates/goth/providers/onedrive"
|
|
"github.com/markbates/goth/providers/openidConnect"
|
|
"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/xero"
|
|
"github.com/markbates/goth/providers/yahoo"
|
|
"github.com/markbates/goth/providers/yammer"
|
|
)
|
|
|
|
var sessionsManager *sessions.Sessions
|
|
|
|
func init() {
|
|
// attach a session manager
|
|
cookieName := "mycustomsessionid"
|
|
hashKey := securecookie.GenerateRandomKey(64)
|
|
blockKey := securecookie.GenerateRandomKey(32)
|
|
secureCookie := securecookie.New(hashKey, blockKey)
|
|
|
|
sessionsManager = sessions.New(sessions.Config{
|
|
Cookie: cookieName,
|
|
Encoding: secureCookie,
|
|
AllowReclaim: true,
|
|
})
|
|
}
|
|
|
|
// These are some function helpers that you may use if you want
|
|
|
|
// GetProviderName is a function used to get the name of a provider
|
|
// for a given request. By default, this provider is fetched from
|
|
// the URL query string. If you provide it in a different way,
|
|
// assign your own function to this variable that returns the provider
|
|
// name for your request.
|
|
var GetProviderName = func(ctx iris.Context) (string, error) {
|
|
// try to get it from the url param "provider"
|
|
if p := ctx.URLParam("provider"); p != "" {
|
|
return p, nil
|
|
}
|
|
|
|
// try to get it from the url PATH parameter "{provider} or :provider or {provider:string} or {provider:alphabetical}"
|
|
if p := ctx.Params().Get("provider"); p != "" {
|
|
return p, nil
|
|
}
|
|
|
|
// try to get it from context's per-request storage
|
|
if p := ctx.Values().GetString("provider"); p != "" {
|
|
return p, nil
|
|
}
|
|
// if not found then return an empty string with the corresponding error
|
|
return "", errors.New("you must select a provider")
|
|
}
|
|
|
|
/*
|
|
BeginAuthHandler is a convenience handler for starting the authentication process.
|
|
It expects to be able to get the name of the provider from the query parameters
|
|
as either "provider" or ":provider".
|
|
|
|
BeginAuthHandler will redirect the user to the appropriate authentication end-point
|
|
for the requested provider.
|
|
|
|
See https://github.com/markbates/goth/examples/main.go to see this in action.
|
|
*/
|
|
func BeginAuthHandler(ctx iris.Context) {
|
|
url, err := GetAuthURL(ctx)
|
|
if err != nil {
|
|
ctx.StatusCode(iris.StatusBadRequest)
|
|
ctx.Writef("%v", err)
|
|
return
|
|
}
|
|
|
|
ctx.Redirect(url, iris.StatusTemporaryRedirect)
|
|
}
|
|
|
|
/*
|
|
GetAuthURL starts the authentication process with the requested provided.
|
|
It will return a URL that should be used to send users to.
|
|
|
|
It expects to be able to get the name of the provider from the query parameters
|
|
as either "provider" or ":provider" or from the context's value of "provider" key.
|
|
|
|
I would recommend using the BeginAuthHandler instead of doing all of these steps
|
|
yourself, but that's entirely up to you.
|
|
*/
|
|
func GetAuthURL(ctx iris.Context) (string, error) {
|
|
providerName, err := GetProviderName(ctx)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
provider, err := goth.GetProvider(providerName)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
sess, err := provider.BeginAuth(SetState(ctx))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
url, err := sess.GetAuthURL()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
session := sessionsManager.Start(ctx)
|
|
session.Set(providerName, sess.Marshal())
|
|
return url, nil
|
|
}
|
|
|
|
// SetState sets the state string associated with the given request.
|
|
// If no state string is associated with the request, one will be generated.
|
|
// This state is sent to the provider and can be retrieved during the
|
|
// callback.
|
|
var SetState = func(ctx iris.Context) string {
|
|
state := ctx.URLParam("state")
|
|
if len(state) > 0 {
|
|
return state
|
|
}
|
|
|
|
return "state"
|
|
}
|
|
|
|
// GetState gets the state returned by the provider during the callback.
|
|
// This is used to prevent CSRF attacks, see
|
|
// http://tools.ietf.org/html/rfc6749#section-10.12
|
|
var GetState = func(ctx iris.Context) string {
|
|
return ctx.URLParam("state")
|
|
}
|
|
|
|
/*
|
|
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 query parameters
|
|
as either "provider" or ":provider".
|
|
|
|
See https://github.com/markbates/goth/examples/main.go to see this in action.
|
|
*/
|
|
var CompleteUserAuth = func(ctx iris.Context) (goth.User, error) {
|
|
providerName, err := GetProviderName(ctx)
|
|
if err != nil {
|
|
return goth.User{}, err
|
|
}
|
|
|
|
provider, err := goth.GetProvider(providerName)
|
|
if err != nil {
|
|
return goth.User{}, err
|
|
}
|
|
session := sessionsManager.Start(ctx)
|
|
value := session.GetString(providerName)
|
|
if value == "" {
|
|
return goth.User{}, errors.New("session value for " + providerName + " not found")
|
|
}
|
|
|
|
sess, err := provider.UnmarshalSession(value)
|
|
if err != nil {
|
|
return goth.User{}, err
|
|
}
|
|
|
|
user, err := provider.FetchUser(sess)
|
|
if err == nil {
|
|
// user can be found with existing session data
|
|
return user, err
|
|
}
|
|
|
|
// get new token and retry fetch
|
|
_, err = sess.Authorize(provider, ctx.Request().URL.Query())
|
|
if err != nil {
|
|
return goth.User{}, err
|
|
}
|
|
|
|
session.Set(providerName, sess.Marshal())
|
|
return provider.FetchUser(sess)
|
|
}
|
|
|
|
// Logout invalidates a user session.
|
|
func Logout(ctx iris.Context) error {
|
|
providerName, err := GetProviderName(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
session := sessionsManager.Start(ctx)
|
|
session.Delete(providerName)
|
|
return nil
|
|
}
|
|
|
|
// End of the "some function helpers".
|
|
|
|
func main() {
|
|
goth.UseProviders(
|
|
twitter.New(os.Getenv("TWITTER_KEY"), os.Getenv("TWITTER_SECRET"), "http://localhost:3000/auth/twitter/callback"),
|
|
// If you'd like to use authenticate instead of authorize in Twitter provider, use this instead.
|
|
// twitter.NewAuthenticate(os.Getenv("TWITTER_KEY"), os.Getenv("TWITTER_SECRET"), "http://localhost:3000/auth/twitter/callback"),
|
|
|
|
facebook.New(os.Getenv("FACEBOOK_KEY"), os.Getenv("FACEBOOK_SECRET"), "http://localhost:3000/auth/facebook/callback"),
|
|
fitbit.New(os.Getenv("FITBIT_KEY"), os.Getenv("FITBIT_SECRET"), "http://localhost:3000/auth/fitbit/callback"),
|
|
gplus.New(os.Getenv("GPLUS_KEY"), os.Getenv("GPLUS_SECRET"), "http://localhost:3000/auth/gplus/callback"),
|
|
github.New(os.Getenv("GITHUB_KEY"), os.Getenv("GITHUB_SECRET"), "http://localhost:3000/auth/github/callback"),
|
|
spotify.New(os.Getenv("SPOTIFY_KEY"), os.Getenv("SPOTIFY_SECRET"), "http://localhost:3000/auth/spotify/callback"),
|
|
linkedin.New(os.Getenv("LINKEDIN_KEY"), os.Getenv("LINKEDIN_SECRET"), "http://localhost:3000/auth/linkedin/callback"),
|
|
lastfm.New(os.Getenv("LASTFM_KEY"), os.Getenv("LASTFM_SECRET"), "http://localhost:3000/auth/lastfm/callback"),
|
|
twitch.New(os.Getenv("TWITCH_KEY"), os.Getenv("TWITCH_SECRET"), "http://localhost:3000/auth/twitch/callback"),
|
|
dropbox.New(os.Getenv("DROPBOX_KEY"), os.Getenv("DROPBOX_SECRET"), "http://localhost:3000/auth/dropbox/callback"),
|
|
digitalocean.New(os.Getenv("DIGITALOCEAN_KEY"), os.Getenv("DIGITALOCEAN_SECRET"), "http://localhost:3000/auth/digitalocean/callback", "read"),
|
|
bitbucket.New(os.Getenv("BITBUCKET_KEY"), os.Getenv("BITBUCKET_SECRET"), "http://localhost:3000/auth/bitbucket/callback"),
|
|
instagram.New(os.Getenv("INSTAGRAM_KEY"), os.Getenv("INSTAGRAM_SECRET"), "http://localhost:3000/auth/instagram/callback"),
|
|
intercom.New(os.Getenv("INTERCOM_KEY"), os.Getenv("INTERCOM_SECRET"), "http://localhost:3000/auth/intercom/callback"),
|
|
box.New(os.Getenv("BOX_KEY"), os.Getenv("BOX_SECRET"), "http://localhost:3000/auth/box/callback"),
|
|
salesforce.New(os.Getenv("SALESFORCE_KEY"), os.Getenv("SALESFORCE_SECRET"), "http://localhost:3000/auth/salesforce/callback"),
|
|
amazon.New(os.Getenv("AMAZON_KEY"), os.Getenv("AMAZON_SECRET"), "http://localhost:3000/auth/amazon/callback"),
|
|
yammer.New(os.Getenv("YAMMER_KEY"), os.Getenv("YAMMER_SECRET"), "http://localhost:3000/auth/yammer/callback"),
|
|
onedrive.New(os.Getenv("ONEDRIVE_KEY"), os.Getenv("ONEDRIVE_SECRET"), "http://localhost:3000/auth/onedrive/callback"),
|
|
|
|
// Pointed localhost.com to http://localhost:3000/auth/yahoo/callback through proxy as yahoo
|
|
// does not allow to put custom ports in redirection uri
|
|
yahoo.New(os.Getenv("YAHOO_KEY"), os.Getenv("YAHOO_SECRET"), "http://localhost.com"),
|
|
slack.New(os.Getenv("SLACK_KEY"), os.Getenv("SLACK_SECRET"), "http://localhost:3000/auth/slack/callback"),
|
|
stripe.New(os.Getenv("STRIPE_KEY"), os.Getenv("STRIPE_SECRET"), "http://localhost:3000/auth/stripe/callback"),
|
|
wepay.New(os.Getenv("WEPAY_KEY"), os.Getenv("WEPAY_SECRET"), "http://localhost:3000/auth/wepay/callback", "view_user"),
|
|
// By default paypal production auth urls will be used, please set PAYPAL_ENV=sandbox as environment variable for testing
|
|
// in sandbox environment
|
|
paypal.New(os.Getenv("PAYPAL_KEY"), os.Getenv("PAYPAL_SECRET"), "http://localhost:3000/auth/paypal/callback"),
|
|
steam.New(os.Getenv("STEAM_KEY"), "http://localhost:3000/auth/steam/callback"),
|
|
heroku.New(os.Getenv("HEROKU_KEY"), os.Getenv("HEROKU_SECRET"), "http://localhost:3000/auth/heroku/callback"),
|
|
uber.New(os.Getenv("UBER_KEY"), os.Getenv("UBER_SECRET"), "http://localhost:3000/auth/uber/callback"),
|
|
soundcloud.New(os.Getenv("SOUNDCLOUD_KEY"), os.Getenv("SOUNDCLOUD_SECRET"), "http://localhost:3000/auth/soundcloud/callback"),
|
|
gitlab.New(os.Getenv("GITLAB_KEY"), os.Getenv("GITLAB_SECRET"), "http://localhost:3000/auth/gitlab/callback"),
|
|
dailymotion.New(os.Getenv("DAILYMOTION_KEY"), os.Getenv("DAILYMOTION_SECRET"), "http://localhost:3000/auth/dailymotion/callback", "email"),
|
|
deezer.New(os.Getenv("DEEZER_KEY"), os.Getenv("DEEZER_SECRET"), "http://localhost:3000/auth/deezer/callback", "email"),
|
|
discord.New(os.Getenv("DISCORD_KEY"), os.Getenv("DISCORD_SECRET"), "http://localhost:3000/auth/discord/callback", discord.ScopeIdentify, discord.ScopeEmail),
|
|
meetup.New(os.Getenv("MEETUP_KEY"), os.Getenv("MEETUP_SECRET"), "http://localhost:3000/auth/meetup/callback"),
|
|
|
|
// Auth0 allocates domain per customer, a domain must be provided for auth0 to work
|
|
auth0.New(os.Getenv("AUTH0_KEY"), os.Getenv("AUTH0_SECRET"), "http://localhost:3000/auth/auth0/callback", os.Getenv("AUTH0_DOMAIN")),
|
|
xero.New(os.Getenv("XERO_KEY"), os.Getenv("XERO_SECRET"), "http://localhost:3000/auth/xero/callback"),
|
|
)
|
|
|
|
// OpenID Connect is based on OpenID Connect Auto Discovery URL (https://openid.net/specs/openid-connect-discovery-1_0-17.html)
|
|
// because the OpenID Connect provider initialize it self in the New(), it can return an error which should be handled or ignored
|
|
// ignore the error for now
|
|
openidConnect, _ := openidConnect.New(os.Getenv("OPENID_CONNECT_KEY"), os.Getenv("OPENID_CONNECT_SECRET"), "http://localhost:3000/auth/openid-connect/callback", os.Getenv("OPENID_CONNECT_DISCOVERY_URL"))
|
|
if openidConnect != nil {
|
|
goth.UseProviders(openidConnect)
|
|
}
|
|
|
|
m := make(map[string]string)
|
|
m["amazon"] = "Amazon"
|
|
m["bitbucket"] = "Bitbucket"
|
|
m["box"] = "Box"
|
|
m["dailymotion"] = "Dailymotion"
|
|
m["deezer"] = "Deezer"
|
|
m["digitalocean"] = "Digital Ocean"
|
|
m["discord"] = "Discord"
|
|
m["dropbox"] = "Dropbox"
|
|
m["facebook"] = "Facebook"
|
|
m["fitbit"] = "Fitbit"
|
|
m["github"] = "Github"
|
|
m["gitlab"] = "Gitlab"
|
|
m["soundcloud"] = "SoundCloud"
|
|
m["spotify"] = "Spotify"
|
|
m["steam"] = "Steam"
|
|
m["stripe"] = "Stripe"
|
|
m["twitch"] = "Twitch"
|
|
m["uber"] = "Uber"
|
|
m["wepay"] = "Wepay"
|
|
m["yahoo"] = "Yahoo"
|
|
m["yammer"] = "Yammer"
|
|
m["gplus"] = "Google Plus"
|
|
m["heroku"] = "Heroku"
|
|
m["instagram"] = "Instagram"
|
|
m["intercom"] = "Intercom"
|
|
m["lastfm"] = "Last FM"
|
|
m["linkedin"] = "Linkedin"
|
|
m["onedrive"] = "Onedrive"
|
|
m["paypal"] = "Paypal"
|
|
m["twitter"] = "Twitter"
|
|
m["salesforce"] = "Salesforce"
|
|
m["slack"] = "Slack"
|
|
m["meetup"] = "Meetup.com"
|
|
m["auth0"] = "Auth0"
|
|
m["openid-connect"] = "OpenID Connect"
|
|
m["xero"] = "Xero"
|
|
|
|
var keys []string
|
|
for k := range m {
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Strings(keys)
|
|
|
|
providerIndex := &ProviderIndex{Providers: keys, ProvidersMap: m}
|
|
|
|
// create our app,
|
|
// set a view
|
|
// set sessions
|
|
// and setup the router for the showcase
|
|
app := iris.New()
|
|
|
|
// attach and build our templates
|
|
app.RegisterView(iris.HTML("./templates", ".html"))
|
|
|
|
// start of the router
|
|
|
|
app.Get("/auth/{provider}/callback", func(ctx iris.Context) {
|
|
user, err := CompleteUserAuth(ctx)
|
|
if err != nil {
|
|
ctx.StatusCode(iris.StatusInternalServerError)
|
|
ctx.Writef("%v", err)
|
|
return
|
|
}
|
|
ctx.ViewData("", user)
|
|
if err := ctx.View("user.html"); err != nil {
|
|
ctx.Writef("%v", err)
|
|
}
|
|
})
|
|
|
|
app.Get("/logout/{provider}", func(ctx iris.Context) {
|
|
Logout(ctx)
|
|
ctx.Redirect("/", iris.StatusTemporaryRedirect)
|
|
})
|
|
|
|
app.Get("/auth/{provider}", func(ctx iris.Context) {
|
|
// try to get the user without re-authenticating
|
|
if gothUser, err := CompleteUserAuth(ctx); err == nil {
|
|
ctx.ViewData("", gothUser)
|
|
if err := ctx.View("user.html"); err != nil {
|
|
ctx.Writef("%v", err)
|
|
}
|
|
} else {
|
|
BeginAuthHandler(ctx)
|
|
}
|
|
})
|
|
|
|
app.Get("/", func(ctx iris.Context) {
|
|
ctx.ViewData("", providerIndex)
|
|
|
|
if err := ctx.View("index.html"); err != nil {
|
|
ctx.Writef("%v", err)
|
|
}
|
|
})
|
|
|
|
// http://localhost:3000
|
|
app.Listen("localhost:3000")
|
|
}
|
|
|
|
type ProviderIndex struct {
|
|
Providers []string
|
|
ProvidersMap map[string]string
|
|
}
|