# README
OAuth2 - Open Auth 2.0 Client
Installation
To install the package, run:
go get github.com/go-zoox/oauth2
Getting Started
Example 1: Using only one oauth2 provider => doreamon
// step1: create oauth2 middleware/handler
// file: oauth2.go
import (
"log"
"net/http"
"regexp"
"time"
"github.com/go-zoox/logger"
"github.com/go-zoox/oauth2"
"github.com/go-zoox/oauth2/doreamon"
)
type CreateOAuth2DoreamonHandlerConfig struct {
ClientID string
ClientSecret string
RedirectURI string
}
func CreateOAuth2DoreamonHandler(cfg *CreateOAuth2DoreamonHandlerConfig) func(
w http.ResponseWriter,
r *http.Request,
CheckUser func(r *http.Request) error,
RemeberUser func(user *oauth2.User, token *oauth2.Token) error,
Next func() error,
) error {
originPathCookieKey := "login_from"
client, err := doreamon.New(&doreamon.DoreamonConfig{
ClientID: cfg.ClientID,
ClientSecret: cfg.ClientSecret,
RedirectURI: cfg.RedirectURI,
Scope: "using_doreamon",
Version: "2",
})
if err != nil {
panic(err)
}
return func(
w http.ResponseWriter,
r *http.Request,
RestoreUser func(r *http.Request) error,
SaveUser func(user *oauth2.User, token *oauth2.Token) error,
Next func() error,
) error {
if r.Method != "GET" {
return Next()
}
path := r.URL.Path
if path == "/login" {
client.Authorize("memos", func(loginUrl string) {
http.Redirect(w, r, loginUrl, http.StatusFound)
})
return nil
}
if path == "/logout" {
client.Logout(func(logoutUrl string) {
http.Redirect(w, r, logoutUrl, http.StatusFound)
})
return nil
}
if path == "/login/doreamon/callback" {
code := r.FormValue("code")
state := r.FormValue("state")
client.Callback(code, state, func(user *oauth2.User, token *oauth2.Token, err error) {
if err != nil {
log.Println("[OAUTH2] Login Callback Error", err)
time.Sleep(3 * time.Second)
http.Redirect(w, r, "/login", http.StatusFound)
return
}
if err := SaveUser(user, token); err != nil {
logger.Info("failed to save user: %#v", err)
time.Sleep(1)
w.WriteHeader(500)
w.Write([]byte("Failed to create user: " + user.Email))
return
}
http.Redirect(w, r, "/", http.StatusFound)
})
return nil
}
if matched, _ := regexp.MatchString("\\.(js|css|json)$", path); err == nil && matched {
return Next()
}
if err := RestoreUser(r); err != nil {
logger.Info("failed to restart user: %#v", err)
time.Sleep(1)
http.SetCookie(w, &http.Cookie{
Name: "OriginPath",
Value: path,
})
http.Redirect(w, r, "/login", http.StatusFound)
return nil
}
// success
if OriginPath, err := r.Cookie(originPathCookieKey); err == nil && OriginPath.Value != "" {
time.Sleep(1)
http.SetCookie(w, &http.Cookie{
Name: originPathCookieKey,
Value: "",
Expires: time.Unix(0, 0),
})
http.Redirect(w, r, OriginPath.Value, http.StatusFound)
return nil
}
return Next()
}
}
// step 2: use as go http middleware
// here is memos/echo
e.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
if os.Getenv("DOREAMON_CLIENT_ID") == "" {
panic("env DOREAMON_CLIENT_ID is required")
}
if os.Getenv("DOREAMON_CLIENT_SECRET") == "" {
panic("env DOREAMON_CLIENT_SECRET is required")
}
if os.Getenv("DOREAMON_REDIRECT_URI") == "" {
panic("env DOREAMON_REDIRECT_URI is required")
}
handler := CreateOAuth2DoreamonHandler(&CreateOAuth2DoreamonHandlerConfig{
ClientID: os.Getenv("DOREAMON_CLIENT_ID"),
ClientSecret: os.Getenv("DOREAMON_CLIENT_SECRET"),
RedirectURI: os.Getenv("DOREAMON_REDIRECT_URI"),
})
return func(c echo.Context) error {
return handler(
c.Response().Writer,
c.Request(),
func(r *http.Request) error {
userID, ok := getUserSession(c)
if !ok {
return fmt.Errorf("no user session found")
}
c.Set(getUserIDContextKey(), userID)
userFind := &api.UserFind{
ID: &userID,
}
_, err := s.Store.FindUser(c.Request().Context(), userFind)
if err != nil {
return err
}
return nil
},
func(user *oauth2.User, token *oauth2.Token) error {
ctx := c.Request().Context()
// Get Or Create User
userFind := &api.UserFind{
Username: &user.Email,
}
dbUser, err := s.Store.FindUser(ctx, userFind)
if err != nil || dbUser == nil {
role := api.Host
hostUserFind := api.UserFind{
Role: &role,
}
hostUser, err := s.Store.FindUser(ctx, &hostUserFind)
if err != nil {
return err
}
if hostUser != nil {
role = api.NormalUser
}
userCreate := &api.UserCreate{
Username: user.Email,
Role: api.Role(role),
Nickname: user.Nickname,
Password: random.String(32),
OpenID: common.GenUUID(),
}
dbUser, err = s.Store.CreateUser(ctx, userCreate)
if err != nil {
return err
}
}
if err = setUserSession(c, dbUser); err != nil {
return err
}
return nil
},
func() error {
return next(c)
},
)
}
})
Example 2: Support multiple oauth2 providers: github, wechat, gitee, doreamon
// @TODO connect
License
GoZoox is released under the MIT License.
# Packages
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
# Functions
ApplyDefaultConfig applies the default config.
Get gets the oauth2 service provider by name.
GetToken gets the token by code and state.
GetUser gets the user by token.
New creates a OAuth2 client.
RefreshToken refresh the token by refresh token.
Register registers a new oauth2 service provider.
ValidateConfig validates the config.
# Variables
ErrConfigAuthURLEmpty is the error of AuthURL is empty.
ErrConfigClientIDEmpty is the error of ClientID is empty.
ErrConfigClientSecretEmpty is the error of ClientSecret is empty.
ErrConfigRedirectURIEmpty is the error of RedirectURI is empty.
ErrConfigTokenURLEmpty is the error of TokenURL is empty.
ErrConfigUserInfoURLEmpty is the error of UserInfoURL is empty.
Version is the version of this package.
# Structs
Config is the OAuth2 config.
StepCallback is the callback ...
Token is the oauth2 token.
User is the oauth2 user.
# Interfaces
Client is the oauth2 client interface.