Skip to content

Commit

Permalink
feat: Add OAuth app loading from config and user info endpoint (#14)
Browse files Browse the repository at this point in the history
- Implement LoadOAuthAppFromConfig to support loading OAuth app from configuration
- Add /v1/users/me endpoint to retrieve current user information associated with the token
  • Loading branch information
wasd96040501 authored Feb 5, 2025
1 parent 5259c14 commit 5c21d40
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 0 deletions.
56 changes: 56 additions & 0 deletions auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -739,3 +739,59 @@ func parsePrivateKey(privateKeyPEM string) (*rsa.PrivateKey, error) {

return rsaKey, nil
}

// OAuthConfig represents the configuration for OAuth clients
type OAuthConfig struct {
ClientID string `json:"client_id"`
ClientType string `json:"client_type"`
ClientSecret string `json:"client_secret,omitempty"`
PrivateKey string `json:"private_key,omitempty"`
PublicKeyID string `json:"public_key_id,omitempty"`
CozeAPIBase string `json:"coze_api_base,omitempty"`
CozeWWWBase string `json:"coze_www_base,omitempty"`
}

// LoadOAuthAppFromConfig creates an OAuth client based on the provided JSON configuration bytes
func LoadOAuthAppFromConfig(config *OAuthConfig) (interface{}, error) {
if config.ClientID == "" {
return nil, errors.New("client_id is required")
}

if config.ClientType == "" {
return nil, errors.New("client_type is required")
}

var opts []OAuthClientOption
if config.CozeAPIBase != "" {
opts = append(opts, WithAuthBaseURL(config.CozeAPIBase))
}
if config.CozeWWWBase != "" {
opts = append(opts, WithAuthWWWURL(config.CozeWWWBase))
}

switch config.ClientType {
case "pkce":
return NewPKCEOAuthClient(config.ClientID, opts...)
case "jwt":
if config.PrivateKey == "" {
return nil, errors.New("private_key is required for JWT client")
}
if config.PublicKeyID == "" {
return nil, errors.New("public_key_id is required for JWT client")
}
return NewJWTOAuthClient(NewJWTOAuthClientParam{
ClientID: config.ClientID,
PublicKey: config.PublicKeyID,
PrivateKeyPEM: config.PrivateKey,
}, opts...)
case "device":
return NewDeviceOAuthClient(config.ClientID, opts...)
case "web":
if config.ClientSecret == "" {
return nil, errors.New("client_secret is required for Web client")
}
return NewWebOAuthClient(config.ClientID, config.ClientSecret, opts...)
default:
return nil, fmt.Errorf("invalid OAuth client_type: %s", config.ClientType)
}
}
2 changes: 2 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type CozeAPI struct {
Datasets *datasets
Files *files
Templates *templates
Users *users
baseURL string
}

Expand Down Expand Up @@ -84,6 +85,7 @@ func NewCozeAPI(auth Auth, opts ...CozeAPIOption) CozeAPI {
Datasets: newDatasets(core),
Files: newFiles(core),
Templates: newTemplates(core),
Users: newUsers(core),
baseURL: opt.baseURL,
}
return cozeClient
Expand Down
43 changes: 43 additions & 0 deletions users.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package coze

import (
"context"
"net/http"
)

// User represents a Coze user
type User struct {
baseModel
UserID string `json:"user_id"`
UserName string `json:"user_name"`
NickName string `json:"nick_name"`
AvatarURL string `json:"avatar_url"`
}

type meResp struct {
baseResponse
User *User `json:"data"`
}

type users struct {
client *core
}

func newUsers(core *core) *users {
return &users{
client: core,
}
}

// Me retrieves the current user's information
func (r *users) Me(ctx context.Context) (*User, error) {
method := http.MethodGet
uri := "/v1/users/me"
resp := &meResp{}
if err := r.client.Request(ctx, method, uri, nil, resp); err != nil {
return nil, err
}

resp.User.setHTTPResponse(resp.HTTPResponse)
return resp.User, nil
}
38 changes: 38 additions & 0 deletions users_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package coze

import (
"context"
"net/http"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestUsersClient_Me(t *testing.T) {
mockTransport := &mockTransport{
roundTripFunc: func(req *http.Request) (*http.Response, error) {
expectedUser := &meResp{
User: &User{
UserID: "test_user_id",
UserName: "test_user",
NickName: "Test User",
AvatarURL: "https://example.com/avatar.jpg",
},
}
return mockResponse(http.StatusOK, expectedUser)
},
}

client := NewCozeAPI(NewTokenAuth("test_token"),
WithBaseURL(ComBaseURL),
WithHttpClient(&http.Client{Transport: mockTransport}),
)

user, err := client.Users.Me(context.Background())
require.NoError(t, err)
assert.Equal(t, "test_user_id", user.UserID)
assert.Equal(t, "test_user", user.UserName)
assert.Equal(t, "Test User", user.NickName)
assert.Equal(t, "https://example.com/avatar.jpg", user.AvatarURL)
}

0 comments on commit 5c21d40

Please sign in to comment.