# 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
# 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.