-
Notifications
You must be signed in to change notification settings - Fork 1.2k
feat: add user_token_getter_url, people can get user token from app manager #686
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,134 @@ | ||
| package auth | ||
|
|
||
| // this file is a demo for user_token_getter_url | ||
|
|
||
| import ( | ||
| "context" | ||
| "encoding/json" | ||
| "fmt" | ||
| "net/http" | ||
| "net/url" | ||
| "strconv" | ||
|
|
||
| "github.com/cloudwego/hertz/pkg/app" | ||
| "github.com/cloudwego/hertz/pkg/common/utils" | ||
| lark "github.com/larksuite/oapi-sdk-go/v3" | ||
| larkauthen "github.com/larksuite/oapi-sdk-go/v3/service/authen/v1" | ||
| ) | ||
|
|
||
| const ( | ||
| appID = "" | ||
| appSecret = "" | ||
| redirectURI | ||
| authHost = "open.feishu.cn/open.larksuite.com" | ||
| ) | ||
|
Comment on lines
+19
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The demo currently builds a broken authorization URL.
Also applies to: 40-50 🤖 Prompt for AI Agents |
||
|
|
||
| var ( | ||
| larkClient = lark.NewClient(appID, appSecret) | ||
| ) | ||
|
|
||
| // UserAuth handles the initial step of the OAuth flow by redirecting the user to the Lark authorization page. | ||
| func UserAuth(ctx context.Context, c *app.RequestContext) { | ||
| state := c.Query("state") | ||
| if state == "" { | ||
| c.JSON(http.StatusBadRequest, utils.H{"error": "missing state"}) | ||
| return | ||
| } | ||
|
|
||
| scope := c.Query("scope") | ||
|
|
||
| authURLObj, _ := url.Parse(fmt.Sprintf("https://%s/open-apis/authen/v1/index", authHost)) | ||
| query := authURLObj.Query() | ||
| query.Set("redirect_uri", redirectURI) | ||
| query.Set("app_id", appID) | ||
| query.Set("state", state) | ||
| if scope != "" { | ||
| query.Set("scope", scope) | ||
| } | ||
| authURLObj.RawQuery = query.Encode() | ||
|
|
||
| c.Redirect(http.StatusFound, []byte(authURLObj.String())) | ||
| } | ||
|
|
||
| // OAuthCallback processes the OAuth callback from Lark, fetches the user access token, and sends it back to the local server. | ||
| func OAuthCallback(ctx context.Context, c *app.RequestContext) { | ||
| code := c.Query("code") | ||
| state := c.Query("state") | ||
|
|
||
| if code == "" { | ||
| c.JSON(http.StatusBadRequest, utils.H{"error": "missing code"}) | ||
| return | ||
| } | ||
|
|
||
| stateInt, err := strconv.Atoi(state) | ||
| if err != nil { | ||
| c.JSON(http.StatusBadRequest, utils.H{"error": "invalid state parameter, must be integer"}) | ||
| return | ||
|
Comment on lines
+63
to
+66
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Keep the callback port separate from OAuth Here Also applies to: 102-131 🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| // get user_access_token | ||
| req := larkauthen.NewCreateAccessTokenReqBuilder(). | ||
| Body(larkauthen.NewCreateAccessTokenReqBodyBuilder(). | ||
| GrantType("authorization_code"). | ||
| Code(code). | ||
| Build()). | ||
| Build() | ||
|
|
||
| resp, err := larkClient.Authen.AccessToken.Create(ctx, req) | ||
| if err != nil { | ||
| c.JSON(http.StatusInternalServerError, utils.H{"error": "failed to get access token", "detail": err.Error()}) | ||
| return | ||
| } | ||
|
|
||
| if !resp.Success() { | ||
| c.JSON(http.StatusInternalServerError, utils.H{"error": "feishu API returned error", "code": resp.Code, "msg": resp.Msg}) | ||
| return | ||
| } | ||
|
|
||
| data := resp.Data | ||
| if data == nil || data.AccessToken == nil { | ||
| c.JSON(http.StatusInternalServerError, utils.H{"error": "empty data in response"}) | ||
| return | ||
| } | ||
|
|
||
| // TODO check user permission or scope | ||
|
|
||
| dataBytes, err := json.Marshal(data) | ||
| if err != nil { | ||
| c.JSON(http.StatusInternalServerError, utils.H{"error": "failed to marshal data"}) | ||
| return | ||
| } | ||
|
|
||
| html := fmt.Sprintf(`<!DOCTYPE html> | ||
| <html> | ||
| <head> | ||
| <meta charset="UTF-8"> | ||
| <title>Lark Token</title> | ||
| </head> | ||
| <body> | ||
| <p>Sending token...</p> | ||
| <script> | ||
| window.onload = function() { | ||
| var url = 'http://127.0.0.1:%d/user_access_token'; | ||
| var data = %s; | ||
| fetch(url, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json' | ||
| }, | ||
| body: JSON.stringify(data) | ||
| }).then(function(response) { | ||
| document.body.innerHTML += '<p>Token sended,close in 3 seconds...</p>'; | ||
| setTimeout(function() { | ||
| window.close(); | ||
| }, 3000); | ||
| }).catch(function(err) { | ||
| document.body.innerHTML += '<p>Token send fail: ' + err + '</p>'; | ||
| }); | ||
| }; | ||
| </script> | ||
| </body> | ||
| </html>`, stateInt, string(dataBytes)) | ||
|
Comment on lines
+102
to
+131
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Embedding marshaled JSON directly into a
🛡️ Safer embedding- dataBytes, err := json.Marshal(data)
+ var buf bytes.Buffer
+ enc := json.NewEncoder(&buf)
+ enc.SetEscapeHTML(true)
+ if err := enc.Encode(data); err != nil {
+ c.JSON(http.StatusInternalServerError, utils.H{"error": "failed to marshal data"})
+ return
+ }
+ // Defensively neutralize any "</" sequences before injecting into <script>.
+ dataBytes := bytes.ReplaceAll(bytes.TrimRight(buf.Bytes(), "\n"), []byte("</"), []byte("<\\/"))(Add Also, line 121 has a small typo: 🤖 Prompt for AI Agents |
||
|
|
||
| c.Data(http.StatusOK, "text/html; charset=utf-8", []byte(html)) | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.