wip: 继续 oauth
This commit is contained in:
34
internal/client/client.go
Normal file
34
internal/client/client.go
Normal file
@ -0,0 +1,34 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/authhandler"
|
||||
)
|
||||
|
||||
var (
|
||||
State = uuid.New().String()[:8]
|
||||
)
|
||||
|
||||
func Run(ctx context.Context) error {
|
||||
oauth2.NewClient(ctx, authhandler.TokenSource(
|
||||
ctx,
|
||||
&oauth2.Config{
|
||||
ClientID: "",
|
||||
ClientSecret: "",
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: "",
|
||||
DeviceAuthURL: "",
|
||||
TokenURL: "",
|
||||
AuthStyle: 0,
|
||||
},
|
||||
RedirectURL: "",
|
||||
Scopes: nil,
|
||||
},
|
||||
State,
|
||||
func(authCodeURL string) (code string, state string, err error) {
|
||||
|
||||
},
|
||||
))
|
||||
}
|
52
internal/client/login.html
Normal file
52
internal/client/login.html
Normal file
@ -0,0 +1,52 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"
|
||||
>
|
||||
<title>Client Login</title>
|
||||
<style>
|
||||
body {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<h3>这里是 xx 产品登录页面</h3>
|
||||
<form>
|
||||
<fieldset>
|
||||
<label>
|
||||
Username
|
||||
<input
|
||||
name="username"
|
||||
placeholder="username"
|
||||
autocomplete="given-name"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Password
|
||||
<input
|
||||
type="password"
|
||||
name="password"
|
||||
placeholder="password"
|
||||
autocomplete="password"
|
||||
/>
|
||||
</label>
|
||||
</fieldset>
|
||||
|
||||
<input
|
||||
type="submit"
|
||||
value="登录"
|
||||
/>
|
||||
<a href="http://localhost:8080/api/oauth/v2/login?client_id=test&client_secret=test&scope=test1,test2&redirect_uri=http://localhost:9119/api/login/callback">使用 Pro 账号登录</a>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
39
internal/serve/handler/authorize.go
Normal file
39
internal/serve/handler/authorize.go
Normal file
@ -0,0 +1,39 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/loveuer/nf"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func Authorize(c *nf.Ctx) error {
|
||||
|
||||
// 解析查询参数
|
||||
clientID := c.Query("client_id")
|
||||
responseType := c.Query("response_type")
|
||||
redirectURI := c.Query("redirect_uri")
|
||||
scope := c.Query("scope")
|
||||
|
||||
// 检查客户端 ID 和其他参数
|
||||
// 在实际应用中,你需要检查这些参数是否合法
|
||||
if clientID != "12345" || responseType != "code" || redirectURI != "http://localhost:8080/callback" {
|
||||
return c.Status(http.StatusBadRequest).SendString("Invalid request")
|
||||
}
|
||||
|
||||
// 显示授权页面给用户
|
||||
_, err := c.Write([]byte(`
|
||||
<html>
|
||||
<head><title>Authorization</title></head>
|
||||
<body>
|
||||
<h1>Do you want to authorize this application?</h1>
|
||||
<form action="/approve" method="post">
|
||||
<input type="hidden" name="client_id" value="` + clientID + `"/>
|
||||
<input type="hidden" name="redirect_uri" value="` + redirectURI + `"/>
|
||||
<input type="hidden" name="scope" value="` + scope + `"/>
|
||||
<button type="submit">Yes, I authorize</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
`))
|
||||
|
||||
return err
|
||||
}
|
77
internal/serve/handler/login.go
Normal file
77
internal/serve/handler/login.go
Normal file
@ -0,0 +1,77 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"github.com/loveuer/nf"
|
||||
"github.com/loveuer/nf/nft/resp"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed serve_login.html
|
||||
page string
|
||||
)
|
||||
|
||||
func LoginPage(c *nf.Ctx) error {
|
||||
type Req struct {
|
||||
ClientID string `query:"client_id"`
|
||||
ClientSecret string `query:"client_secret"`
|
||||
Scope string `query:"scope"`
|
||||
RedirectURI string `query:"redirect_uri"`
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
req = new(Req)
|
||||
)
|
||||
|
||||
if err = c.QueryParser(req); err != nil {
|
||||
return resp.Resp400(c, err.Error())
|
||||
}
|
||||
|
||||
if req.ClientID == "" || req.ClientSecret == "" || req.RedirectURI == "" {
|
||||
return resp.Resp400(c, req)
|
||||
}
|
||||
|
||||
// todo: 验证 client id, client secret, scoop
|
||||
|
||||
// todo: 如果用户是已登录状态,则直接带上信息返回到 authorize 页面
|
||||
|
||||
return c.RenderHTML("login", page, map[string]interface{}{
|
||||
"client_id": req.ClientID,
|
||||
"client_secret": req.ClientSecret,
|
||||
"redirect_uri": req.RedirectURI,
|
||||
"scope": req.Scope,
|
||||
})
|
||||
}
|
||||
|
||||
func LoginAction(c *nf.Ctx) error {
|
||||
type Req struct {
|
||||
Username string `form:"username"`
|
||||
Password string `form:"password"`
|
||||
ClientId string `form:"client_id"`
|
||||
ClientSecret string `form:"client_secret"`
|
||||
RedirectURI string `form:"redirect_uri"`
|
||||
Scope string `form:"scope"`
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
req = new(Req)
|
||||
)
|
||||
|
||||
if err = c.BodyParser(req); err != nil {
|
||||
return resp.Resp400(c, err.Error())
|
||||
}
|
||||
|
||||
// todo: 验证用户登录是否成功,等等
|
||||
|
||||
queries := make(url.Values)
|
||||
queries.Add("client_id", req.ClientId)
|
||||
queries.Add("client_secret", req.ClientSecret)
|
||||
queries.Add("redirect_uri", req.RedirectURI)
|
||||
queries.Add("scope", req.Scope)
|
||||
|
||||
return c.Redirect("/api/oauth/v2/authorize?"+queries.Encode(), http.StatusFound)
|
||||
}
|
55
internal/serve/handler/serve_login.html
Normal file
55
internal/serve/handler/serve_login.html
Normal file
@ -0,0 +1,55 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.jade.min.css"
|
||||
>
|
||||
<title>Server Login</title>
|
||||
<style>
|
||||
body {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<h3>欢迎来到 Pro</h3>
|
||||
<form action="/api/oauth/v2/login" method="POST">
|
||||
<fieldset>
|
||||
<label>
|
||||
Username
|
||||
<input
|
||||
name="username"
|
||||
placeholder="username"
|
||||
autocomplete="given-name"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Password
|
||||
<input
|
||||
type="password"
|
||||
name="password"
|
||||
placeholder="password"
|
||||
autocomplete="password"
|
||||
/>
|
||||
</label>
|
||||
<input type="hidden" name="client_id" value="{{ .client_id }}"/>
|
||||
<input type="hidden" name="client_secret" value="{{ .client_secret }}"/>
|
||||
<input type="hidden" name="redirect_uri" value="{{ .redirect_uri }}"/>
|
||||
<input type="hidden" name="scope" value="{{ .scope }}"/>
|
||||
</fieldset>
|
||||
|
||||
<input
|
||||
type="submit"
|
||||
value="登录"
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -9,6 +9,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
"uauth/internal/opt"
|
||||
"uauth/internal/serve/handler"
|
||||
"uauth/internal/store/cache"
|
||||
"uauth/internal/tool"
|
||||
"uauth/model"
|
||||
@ -41,39 +42,6 @@ func handleLogin(c *nf.Ctx) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 处理授权请求
|
||||
func handleAuthorize(c *nf.Ctx) error {
|
||||
// 解析查询参数
|
||||
clientID := c.Query("client_id")
|
||||
responseType := c.Query("response_type")
|
||||
redirectURI := c.Query("redirect_uri")
|
||||
scope := c.Query("scope")
|
||||
|
||||
// 检查客户端 ID 和其他参数
|
||||
// 在实际应用中,你需要检查这些参数是否合法
|
||||
if clientID != "12345" || responseType != "code" || redirectURI != "http://localhost:8080/callback" {
|
||||
return c.Status(http.StatusBadRequest).SendString("Invalid request")
|
||||
}
|
||||
|
||||
// 显示授权页面给用户
|
||||
_, err := c.Write([]byte(`
|
||||
<html>
|
||||
<head><title>Authorization</title></head>
|
||||
<body>
|
||||
<h1>Do you want to authorize this application?</h1>
|
||||
<form action="/approve" method="post">
|
||||
<input type="hidden" name="client_id" value="` + clientID + `"/>
|
||||
<input type="hidden" name="redirect_uri" value="` + redirectURI + `"/>
|
||||
<input type="hidden" name="scope" value="` + scope + `"/>
|
||||
<button type="submit">Yes, I authorize</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
`))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// 处理用户的授权批准
|
||||
func handleApprove(c *nf.Ctx) error {
|
||||
// 获取表单数据
|
||||
@ -164,8 +132,9 @@ func Run(ctx context.Context) error {
|
||||
|
||||
api := app.Group(opt.Cfg.Svc.Prefix)
|
||||
// 设置路由
|
||||
api.Get("/login", handleLogin)
|
||||
api.Get("/authorize", handleAuthorize)
|
||||
api.Get("/login", handler.LoginPage)
|
||||
api.Post("/login", handler.LoginAction)
|
||||
api.Get("/authorize", handler.Authorize)
|
||||
api.Post("/approve", handleApprove)
|
||||
api.Post("/token", handleToken)
|
||||
|
||||
|
Reference in New Issue
Block a user