2024-10-23 17:46:15 +08:00
|
|
|
package client
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
_ "embed"
|
|
|
|
"encoding/json"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
"github.com/loveuer/nf"
|
|
|
|
"github.com/loveuer/nf/nft/log"
|
|
|
|
"github.com/loveuer/nf/nft/resp"
|
|
|
|
"golang.org/x/oauth2"
|
|
|
|
"net/http"
|
|
|
|
"uauth/internal/tool"
|
|
|
|
)
|
|
|
|
|
|
|
|
//go:embed login.html
|
|
|
|
var page string
|
|
|
|
|
|
|
|
var (
|
|
|
|
config = oauth2.Config{
|
|
|
|
ClientID: "test",
|
|
|
|
ClientSecret: "Foobar123",
|
|
|
|
Endpoint: oauth2.Endpoint{
|
|
|
|
AuthURL: "http://localhost:8080/oauth/v2/authorize",
|
|
|
|
TokenURL: "http://localhost:8080/oauth/v2/token",
|
|
|
|
},
|
|
|
|
RedirectURL: "http://localhost:18080/oauth/v2/redirect",
|
|
|
|
Scopes: []string{"test"},
|
|
|
|
}
|
|
|
|
state = uuid.New().String()[:8]
|
|
|
|
)
|
|
|
|
|
|
|
|
func Run(ctx context.Context) error {
|
|
|
|
app := nf.New()
|
2024-10-31 14:35:52 +08:00
|
|
|
app.Get("/login", handleLoginPage)
|
|
|
|
app.Post("/login", handleLoginAction)
|
2024-10-23 17:46:15 +08:00
|
|
|
app.Get("/oauth/v2/redirect", handleRedirect)
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
<-ctx.Done()
|
|
|
|
_ = app.Shutdown(tool.Timeout(2))
|
|
|
|
}()
|
|
|
|
|
|
|
|
return app.Run(":18080")
|
|
|
|
}
|
|
|
|
|
2024-10-31 14:35:52 +08:00
|
|
|
func handleLoginAction(c *nf.Ctx) error {
|
|
|
|
type Req struct {
|
|
|
|
Username string `form:"username"`
|
|
|
|
Password string `form:"password"`
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
err error
|
|
|
|
req = new(Req)
|
|
|
|
token *oauth2.Token
|
|
|
|
)
|
|
|
|
|
|
|
|
if err = c.BodyParser(req); err != nil {
|
|
|
|
return c.Status(http.StatusBadRequest).SendString(err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Info("[C] password mode: username = %s, password = %s", req.Username, req.Password)
|
|
|
|
|
|
|
|
if token, err = config.PasswordCredentialsToken(c.Context(), req.Username, req.Password); err != nil {
|
|
|
|
log.Error("[C] config do password token err = %s", err)
|
|
|
|
return c.Status(http.StatusBadRequest).SendString(err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
bs, _ := json.Marshal(token)
|
|
|
|
log.Info("[C] oauth finally token =\n%s", string(bs))
|
|
|
|
|
|
|
|
return resp.Resp200(c, token)
|
|
|
|
}
|
|
|
|
|
|
|
|
func handleLoginPage(c *nf.Ctx) error {
|
2024-10-23 17:46:15 +08:00
|
|
|
if c.Query("oauth") != "" {
|
2024-10-31 14:35:52 +08:00
|
|
|
uri := config.AuthCodeURL(state, oauth2.ApprovalForce)
|
2024-10-23 17:46:15 +08:00
|
|
|
log.Info("[C] oauth config client_secret = %s", config.ClientSecret)
|
|
|
|
log.Info("[C] redirect to oauth2 server uri = %s", uri)
|
|
|
|
return c.Redirect(uri, http.StatusFound)
|
|
|
|
}
|
|
|
|
|
|
|
|
return c.HTML(page)
|
|
|
|
}
|
|
|
|
|
|
|
|
func handleRedirect(c *nf.Ctx) error {
|
|
|
|
type Req struct {
|
|
|
|
State string `query:"state"`
|
|
|
|
Code string `query:"code"`
|
|
|
|
Scope string `query:"scope"`
|
|
|
|
ClientId string `query:"client_id"`
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
err error
|
|
|
|
req = new(Req)
|
|
|
|
token *oauth2.Token
|
|
|
|
)
|
|
|
|
|
|
|
|
if err = c.QueryParser(req); err != nil {
|
|
|
|
return resp.Resp400(c, err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
if req.State != state {
|
|
|
|
log.Error("[C] state mismatch, want = %s, got = %s", state, req.State)
|
|
|
|
return c.Status(http.StatusBadRequest).SendString("Bad Request: state mismatch")
|
|
|
|
}
|
|
|
|
|
|
|
|
if token, err = config.Exchange(c.Context(), req.Code); err != nil {
|
|
|
|
log.Error("[C] oauth config exchange err: %s", err.Error())
|
|
|
|
return resp.Resp500(c, err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
bs, _ := json.Marshal(token)
|
|
|
|
log.Info("[C] oauth finally token =\n%s", string(bs))
|
|
|
|
|
|
|
|
return resp.Resp200(c, token)
|
|
|
|
}
|