Files
ezbookkeeping/pkg/auth/oauth2/nextcloud_oauth2_provider.go
T
2025-10-21 01:52:28 +08:00

111 lines
3.4 KiB
Go

package oauth2
import (
"encoding/json"
"io"
"net/http"
"github.com/mayswind/ezbookkeeping/pkg/core"
"github.com/mayswind/ezbookkeeping/pkg/errs"
"github.com/mayswind/ezbookkeeping/pkg/log"
)
type nextcloudUserInfoResponse struct {
OCS *struct {
Meta *struct {
Status string `json:"status"`
StatusCode int `json:"statuscode"`
} `json:"meta"`
Data *struct {
ID string `json:"id"`
Email string `json:"email"`
DisplayName string `json:"display-name"`
} `json:"data"`
} `json:"ocs"`
}
// NextcloudOAuth2Provider represents Nextcloud OAuth 2.0 provider
type NextcloudOAuth2Provider struct {
baseUrl string
}
// NewNextcloudOAuth2Provider creates a new Nextcloud OAuth 2.0 provider instance
func NewNextcloudOAuth2Provider(baseUrl string) OAuth2Provider {
if baseUrl[len(baseUrl)-1] != '/' {
baseUrl += "/"
}
return &NextcloudOAuth2Provider{
baseUrl: baseUrl,
}
}
// GetAuthUrl returns the authentication url of the Nextcloud provider
func (p *NextcloudOAuth2Provider) GetAuthUrl() string {
return p.baseUrl + "apps/oauth2/authorize"
}
// GetTokenUrl returns the token url of the Nextcloud provider
func (p *NextcloudOAuth2Provider) GetTokenUrl() string {
return p.baseUrl + "apps/oauth2/api/v1/token"
}
// GetUserInfo returns the user info by the Nextcloud provider
func (p *NextcloudOAuth2Provider) GetUserInfo(c core.Context, oauth2Client *http.Client) (*OAuth2UserInfo, error) {
url := p.baseUrl + "ocs/v2.php/cloud/user?format=json"
resp, err := oauth2Client.Get(url)
if err != nil {
log.Errorf(c, "[nextcloud_oauth2_provider.GetUserInfo] failed to get user info response, because %s", err.Error())
return nil, errs.ErrFailedToRequestRemoteApi
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
log.Debugf(c, "[nextcloud_oauth2_provider.GetUserInfo] response is %s", body)
if resp.StatusCode != 200 {
log.Errorf(c, "[nextcloud_oauth2_provider.GetUserInfo] failed to get user info response, because response code is %d", resp.StatusCode)
return nil, errs.ErrFailedToRequestRemoteApi
}
return p.parseUserInfo(c, body)
}
// GetScopes returns the scopes required by the Nextcloud provider
func (p *NextcloudOAuth2Provider) GetScopes() []string {
return []string{"profile", "email"}
}
func (p *NextcloudOAuth2Provider) parseUserInfo(c core.Context, body []byte) (*OAuth2UserInfo, error) {
userInfoResp := &nextcloudUserInfoResponse{}
err := json.Unmarshal(body, &userInfoResp)
if err != nil {
log.Warnf(c, "[nextcloud_oauth2_provider.parseUserInfo] failed to parse user info response body, because %s", err.Error())
return nil, errs.ErrCannotRetrieveUserInfo
}
if userInfoResp.OCS == nil || userInfoResp.OCS.Meta == nil || userInfoResp.OCS.Data == nil {
log.Warnf(c, "[nextcloud_oauth2_provider.parseUserInfo] invalid user info response body")
return nil, errs.ErrCannotRetrieveUserInfo
}
if userInfoResp.OCS.Meta.StatusCode != 200 {
log.Warnf(c, "[nextcloud_oauth2_provider.parseUserInfo] user info response status code is %d", userInfoResp.OCS.Meta.StatusCode)
return nil, errs.ErrCannotRetrieveUserInfo
}
if userInfoResp.OCS.Data.ID == "" {
log.Warnf(c, "[nextcloud_oauth2_provider.parseUserInfo] user info id is empty")
return nil, errs.ErrCannotRetrieveUserInfo
}
return &OAuth2UserInfo{
UserName: userInfoResp.OCS.Data.ID,
Email: userInfoResp.OCS.Data.Email,
NickName: userInfoResp.OCS.Data.DisplayName,
}, nil
}