diff --git a/.config/misso.yml.example b/.config/misso.yml.example index dd581b3..79ef846 100644 --- a/.config/misso.yml.example +++ b/.config/misso.yml.example @@ -7,3 +7,8 @@ misskey: secret: "" hydra: admin_url: "http://localhost:4445" +time: + request_valid: 3600 + login_remember: 600 + consent_remember: 0 + userinfo_cache: 3600 diff --git a/.gitignore b/.gitignore index 820f081..8a9782b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,6 @@ db/ redis/ + +config.yml +misso diff --git a/consts/redis_key.go b/consts/redis_key.go index 1b82715..e7fb534 100644 --- a/consts/redis_key.go +++ b/consts/redis_key.go @@ -1,8 +1,8 @@ package consts const ( - REDIS_KEY_LOGIN_SESSION = "misso:login:%s" // Username, token as value - REDIS_KEY_CONSENT_CSRF = "misso:consent:%s" // Random string, consent challenge as value - REDIS_KEY_SHARE_ACCESS_TOKEN = "misso:share:at:%s" // Subject, access token as value - REDIS_KEY_SHARE_USER_INFO = "misso:share:ui:%s" // Subject, user info as value + REDIS_KEY_LOGIN_SESSION = "misso:login:%s" // Username, token as value + REDIS_KEY_CONSENT_CSRF = "misso:consent:%s" // Random string, consent challenge as value + REDIS_KEY_USER_ACCESS_TOKEN = "misso:user:token:%s" // Subject, access token as value + REDIS_KEY_USER_INFO = "misso:user:info:%s" // Subject, user info as value ) diff --git a/consts/time.go b/consts/time.go index 25dbb44..dda9e94 100644 --- a/consts/time.go +++ b/consts/time.go @@ -1,12 +1,10 @@ package consts -import "time" - const ( - TIME_REQUEST_VALID = 1 * time.Hour + TIME_DEFAULT_REQUEST_VALID = 3600 // 1 Hour - TIME_LOGIN_REMEMBER = 10 * time.Minute - TIME_CONSENT_REMEMBER = 0 // Forever + TIME_DEFAULT_LOGIN_REMEMBER = 600 // 10 Minute + TIME_DEFAULT_CONSENT_REMEMBER = 0 // Forever - TIME_USERINFO_CACHE = 10 * time.Minute + TIME_DEFAULT_USERINFO_CACHE = 3600 // 1 Hour ) diff --git a/handlers/consent/consent_check.go b/handlers/consent/consent_check.go index 7503f44..0b5bd09 100644 --- a/handlers/consent/consent_check.go +++ b/handlers/consent/consent_check.go @@ -5,10 +5,12 @@ import ( "fmt" "github.com/gin-gonic/gin" client "github.com/ory/hydra-client-go/v2" + "misso/config" "misso/consts" "misso/global" "misso/utils" "net/http" + "time" ) func ConsentCheck(ctx *gin.Context) { @@ -56,8 +58,8 @@ func ConsentCheck(ctx *gin.Context) { // Generate CSRF token global.Logger.Debugf("Generating CSRF token...") csrf := utils.RandString(32) - sessKey := fmt.Sprintf(consts.REDIS_KEY_CONSENT_CSRF, csrf) - err := global.Redis.Set(context.Background(), sessKey, oauth2challenge, consts.TIME_REQUEST_VALID).Err() + sessKey := fmt.Sprintf(consts.REDIS_KEY_CONSENT_CSRF, oauth2challenge) + err := global.Redis.Set(context.Background(), sessKey, csrf, time.Duration(config.Config.Time.RequestValid)*time.Second).Err() if err != nil { global.Logger.Errorf("Failed to save csrf into redis with error: %v", err) ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{ @@ -69,7 +71,7 @@ func ConsentCheck(ctx *gin.Context) { // Retrieve context global.Logger.Debugf("Retrieving context...") - userinfoCtx, err := utils.GetUserinfo(*consentReq.Subject) + userinfo, err := utils.GetUserinfo(*consentReq.Subject) if err != nil { global.Logger.Errorf("Failed to retrieve userinfo with error: %v", err) ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{ @@ -81,9 +83,10 @@ func ConsentCheck(ctx *gin.Context) { // Show the consent UI global.Logger.Debugf("Rendering consent UI...") templateFields := gin.H{ - "user": *userinfoCtx, + "user": *userinfo, "challenge": oauth2challenge, "csrf": csrf, + "scopes": consentReq.RequestedScope, } if consentReq.Client.LogoUri != nil && *consentReq.Client.LogoUri != "" { @@ -94,6 +97,12 @@ func ConsentCheck(ctx *gin.Context) { } else { templateFields["clientName"] = *consentReq.Client.ClientId } + if consentReq.Client.PolicyUri != nil && *consentReq.Client.PolicyUri != "" { + templateFields["clientPolicy"] = *consentReq.Client.PolicyUri + } + if consentReq.Client.TosUri != nil && *consentReq.Client.TosUri != "" { + templateFields["clientTos"] = *consentReq.Client.TosUri + } ctx.HTML(http.StatusOK, "consent.tmpl", templateFields) global.Logger.Debugf("User should now see Consent UI.") diff --git a/handlers/consent/consent_confirm.go b/handlers/consent/consent_confirm.go index d93055d..7591eb8 100644 --- a/handlers/consent/consent_confirm.go +++ b/handlers/consent/consent_confirm.go @@ -5,16 +5,17 @@ import ( "fmt" "github.com/gin-gonic/gin" client "github.com/ory/hydra-client-go/v2" + "misso/config" "misso/consts" "misso/global" "net/http" - "time" ) type ConsentConfirmRequest struct { - CSRF string `form:"_csrf"` - Remember bool `form:"remember"` - Action string `form:"action"` + CSRF string `form:"_csrf"` + Challenge string `form:"challenge"` + Remember bool `form:"remember"` + Action string `form:"action"` } func ConsentConfirm(ctx *gin.Context) { @@ -32,16 +33,23 @@ func ConsentConfirm(ctx *gin.Context) { // Validate CSRF global.Logger.Debugf("Validating CSRF...") - sessKey := fmt.Sprintf(consts.REDIS_KEY_CONSENT_CSRF, req.CSRF) - oauth2challenge, err := global.Redis.Get(context.Background(), sessKey).Result() + sessKey := fmt.Sprintf(consts.REDIS_KEY_CONSENT_CSRF, req.Challenge) + csrfSession, err := global.Redis.Get(context.Background(), sessKey).Result() if err != nil { global.Logger.Errorf("Failed to get csrf from redis with error: %v", err) ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{ "error": "Failed to get csrf", }) return + } else if csrfSession != req.CSRF { + ctx.HTML(http.StatusForbidden, "error.tmpl", gin.H{ + "error": "CSRF not match", + }) + return } + oauth2challenge := req.Challenge + // Delete used challenge global.Redis.Del(context.Background(), sessKey) @@ -80,9 +88,9 @@ func ConsentConfirm(ctx *gin.Context) { global.Logger.Debugf("User accepted the request, reporting back to hydra...") global.Logger.Debugf("Initializing ID Token...") - rememberFor := int64(consts.TIME_CONSENT_REMEMBER / time.Second) // Remember forever + rememberFor := config.Config.Time.ConsentRemember // Remember forever acceptReq, _, err := global.Hydra.Admin.OAuth2Api.AcceptOAuth2ConsentRequest(context.Background()).ConsentChallenge(oauth2challenge).AcceptOAuth2ConsentRequest(client.AcceptOAuth2ConsentRequest{ - GrantScope: consentReq.RequestedScope, // TODO: Specify scopes + GrantScope: consentReq.RequestedScope, GrantAccessTokenAudience: consentReq.RequestedAccessTokenAudience, Remember: &req.Remember, RememberFor: &rememberFor, diff --git a/handlers/login/login.go b/handlers/login/login.go index a5bac0f..68756f9 100644 --- a/handlers/login/login.go +++ b/handlers/login/login.go @@ -5,10 +5,12 @@ import ( "fmt" "github.com/gin-gonic/gin" client "github.com/ory/hydra-client-go/v2" + "misso/config" "misso/consts" "misso/global" "misso/misskey" "net/http" + "time" ) func Login(ctx *gin.Context) { @@ -69,7 +71,7 @@ func Login(ctx *gin.Context) { // Save login challenge state into redis (misskey cannot keep state info) sessKey := fmt.Sprintf(consts.REDIS_KEY_LOGIN_SESSION, authSess.Token) - err = global.Redis.Set(context.Background(), sessKey, oauth2challenge, consts.TIME_REQUEST_VALID).Err() + err = global.Redis.Set(context.Background(), sessKey, oauth2challenge, time.Duration(config.Config.Time.RequestValid)*time.Second).Err() if err != nil { global.Logger.Errorf("Failed to save session into redis with error: %v", err) ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{ diff --git a/handlers/login/misskey_auth_callback.go b/handlers/login/misskey_auth_callback.go index bdfc35a..a9c0b32 100644 --- a/handlers/login/misskey_auth_callback.go +++ b/handlers/login/misskey_auth_callback.go @@ -9,9 +9,7 @@ import ( "misso/consts" "misso/global" "misso/misskey" - "misso/utils" "net/http" - "time" ) func MisskeyAuthCallback(ctx *gin.Context) { @@ -62,9 +60,10 @@ func MisskeyAuthCallback(ctx *gin.Context) { return } - userid := fmt.Sprintf("%s@%s", usermeta.User.Username, config.Config.Misskey.Instance) + // Save user key + userIdentifier := fmt.Sprintf("%s@%s", usermeta.User.Username, config.Config.Misskey.Instance) - sessAccessTokenKey := fmt.Sprintf(consts.REDIS_KEY_SHARE_ACCESS_TOKEN, userid) + sessAccessTokenKey := fmt.Sprintf(consts.REDIS_KEY_USER_ACCESS_TOKEN, userIdentifier) err = global.Redis.Set(context.Background(), sessAccessTokenKey, usermeta.AccessToken, 0).Err() if err != nil { global.Logger.Errorf("Failed to save session access token into redis with error: %v", err) @@ -74,24 +73,16 @@ func MisskeyAuthCallback(ctx *gin.Context) { return } - // Save context into redis - err = utils.SaveUserinfo(userid, &usermeta.User) - if err != nil { - global.Logger.Errorf("Failed to save session user info into redis with error: %v", err) - ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{ - "error": "Failed to save userinfo", - }) - return - } - global.Logger.Debugf("User accepted the request, reporting back to hydra...") - remember := true - rememberFor := int64(consts.TIME_LOGIN_REMEMBER / time.Second) - acceptReq, _, err := global.Hydra.Admin.OAuth2Api.AcceptOAuth2LoginRequest(context.Background()).LoginChallenge(oauth2challenge).AcceptOAuth2LoginRequest(client.AcceptOAuth2LoginRequest{ - Subject: userid, - Remember: &remember, - RememberFor: &rememberFor, - }).Execute() + acceptReq := client.AcceptOAuth2LoginRequest{ + Subject: userIdentifier, + } + if config.Config.Time.LoginRemember > 0 { + remember := true + acceptReq.Remember = &remember + acceptReq.RememberFor = &config.Config.Time.LoginRemember + } + acceptRes, _, err := global.Hydra.Admin.OAuth2Api.AcceptOAuth2LoginRequest(context.Background()).LoginChallenge(oauth2challenge).AcceptOAuth2LoginRequest(acceptReq).Execute() if err != nil { global.Logger.Errorf("Failed to accept login request with error: %v", err) ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{ @@ -101,7 +92,7 @@ func MisskeyAuthCallback(ctx *gin.Context) { } // Redirect to target uri - ctx.Redirect(http.StatusTemporaryRedirect, acceptReq.RedirectTo) + ctx.Redirect(http.StatusTemporaryRedirect, acceptRes.RedirectTo) global.Logger.Debugf("User should now be redirecting to target URI.") diff --git a/handlers/user/info.go b/handlers/user/info.go index a318dc4..4ef420c 100644 --- a/handlers/user/info.go +++ b/handlers/user/info.go @@ -4,17 +4,11 @@ import ( "context" "github.com/gin-gonic/gin" "misso/global" - "misso/types" "misso/utils" "net/http" "strings" ) -type UserinfoResponse struct { - types.MisskeyUser - EMail string `json:"email"` -} - func UserInfo(ctx *gin.Context) { // Get token from header accessToken := strings.Replace(ctx.GetHeader("Authorization"), "Bearer ", "", 1) @@ -45,7 +39,7 @@ func UserInfo(ctx *gin.Context) { // Return user info global.Logger.Debugf("Retrieving context...") - userinfoCtx, err := utils.GetUserinfo(*tokenInfo.Sub) + userinfo, err := utils.GetUserinfo(*tokenInfo.Sub) if err != nil { global.Logger.Errorf("Failed to retrieve userinfo with error: %v", err) ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{ @@ -54,9 +48,26 @@ func UserInfo(ctx *gin.Context) { return } - ctx.JSON(http.StatusOK, UserinfoResponse{ - MisskeyUser: *userinfoCtx, - EMail: *tokenInfo.Sub, - }) + userinfoRes := gin.H{} // map[string]interface{} + + // Get scopes + if tokenInfo.Scope != nil && *tokenInfo.Scope != "" { + // Has scopes + scopes := strings.Split(*tokenInfo.Scope, " ") + for _, s := range scopes { + if value, ok := (*userinfo)[s]; ok { + userinfoRes[s] = value + } + + if s == "openid" { + userinfoRes["sub"] = *tokenInfo.Sub + } else if s == "username" { + // Add "nickname" field for OIDC compatibility + userinfoRes["nickname"] = userinfoRes[s] + } + } + } + + ctx.JSON(http.StatusOK, userinfoRes) } diff --git a/inits/config.go b/inits/config.go index f7b74d7..a63c42a 100644 --- a/inits/config.go +++ b/inits/config.go @@ -3,6 +3,7 @@ package inits import ( "gopkg.in/yaml.v3" "misso/config" + "misso/consts" "os" ) @@ -23,5 +24,21 @@ func Config() error { return err } + // Validate time + if config.Config.Time.RequestValid <= 0 { + config.Config.Time.RequestValid = consts.TIME_DEFAULT_REQUEST_VALID + } + if config.Config.Time.LoginRemember < 0 { + // 0 means don't remember (in extreme account switch situations) + config.Config.Time.LoginRemember = consts.TIME_DEFAULT_LOGIN_REMEMBER + } + if config.Config.Time.ConsentRemember < 0 { + // 0 means remember forever (default behavior) + config.Config.Time.ConsentRemember = consts.TIME_DEFAULT_CONSENT_REMEMBER + } + if config.Config.Time.UserinfoCache <= 0 { + config.Config.Time.UserinfoCache = consts.TIME_DEFAULT_USERINFO_CACHE + } + return nil } diff --git a/misskey/base.go b/misskey/base.go index 1651a51..f3bfdf5 100644 --- a/misskey/base.go +++ b/misskey/base.go @@ -22,7 +22,7 @@ func PostAPIRequest[T I_Response | AuthSessionGenerate_Response | AuthSessionUse apiEndpointPath string, reqBody any, ) (*T, error) { // Prepare request - apiEndpoint := fmt.Sprintf("https://%s%s", config.Config.Misskey.Instance, apiEndpointPath) + apiEndpoint := fmt.Sprintf("https://%s/api/%s", config.Config.Misskey.Instance, apiEndpointPath) reqBodyBytes, err := json.Marshal(reqBody) if err != nil { diff --git a/misskey/generate_auth_session.go b/misskey/generate_auth_session.go index 4bb33c4..07ecb03 100644 --- a/misskey/generate_auth_session.go +++ b/misskey/generate_auth_session.go @@ -13,7 +13,7 @@ type AuthSessionGenerate_Response struct { func GenerateAuthSession() (*AuthSessionGenerate_Response, error) { - return PostAPIRequest[AuthSessionGenerate_Response]("/api/auth/session/generate", &AuthSessionGenerate_Request{ + return PostAPIRequest[AuthSessionGenerate_Response]("auth/session/generate", &AuthSessionGenerate_Request{ AppSecret: config.Config.Misskey.Application.Secret, }) diff --git a/misskey/get_userinfo.go b/misskey/get_userinfo.go index 07120dd..c97b9e6 100644 --- a/misskey/get_userinfo.go +++ b/misskey/get_userinfo.go @@ -9,7 +9,7 @@ type I_Request struct { type I_Response = types.MisskeyUser func GetUserinfo(accessToken string) (*I_Response, error) { - return PostAPIRequest[I_Response]("/api/i", &I_Request{ + return PostAPIRequest[I_Response]("i", &I_Request{ I: accessToken, }) } diff --git a/misskey/get_userkey.go b/misskey/get_userkey.go index a1befd1..2f16e1b 100644 --- a/misskey/get_userkey.go +++ b/misskey/get_userkey.go @@ -11,13 +11,13 @@ type AuthSessionUserkey_Request struct { } type AuthSessionUserkey_Response struct { - AccessToken string `json:"accessToken"` - User types.MisskeyUser `json:"user"` + AccessToken string `json:"accessToken"` + User types.MisskeyUserBase `json:"user"` } func GetUserkey(token string) (*AuthSessionUserkey_Response, error) { - return PostAPIRequest[AuthSessionUserkey_Response]("/api/auth/session/userkey", &AuthSessionUserkey_Request{ + return PostAPIRequest[AuthSessionUserkey_Response]("auth/session/userkey", &AuthSessionUserkey_Request{ AppSecret: config.Config.Misskey.Application.Secret, Token: token, }) diff --git a/templates/consent.tmpl b/templates/consent.tmpl index 6e6c878..9e0e0d5 100644 --- a/templates/consent.tmpl +++ b/templates/consent.tmpl @@ -1,11 +1,60 @@ {{ define "consent.tmpl" }} - + - 授权确认 + Confirm authorization {{ template "head.tmpl" }} @@ -67,29 +116,69 @@
+ - {{ if .logo }} - {{ .clientName }} - {{ else }} - c-warning - {{ end }} + -

- 应用程序 - {{ .clientName }} - 正请求读取 - {{ .user.Name }} - 的信息 -

+
+

+ Application + {{ .clientName }} + is requesting +
+ access to account + {{ .user.name }} + with the following scopes: +

-

- - -

+ + + {{ if or .clientPolicy .clientTos }} + + {{ end }} +
+ +

- - + +

diff --git a/templates/error.tmpl b/templates/error.tmpl index a5c80ef..722bfa6 100644 --- a/templates/error.tmpl +++ b/templates/error.tmpl @@ -1,14 +1,30 @@ {{ define "error.tmpl" }} - + - 出错了 + Error {{ template "head.tmpl" }} +
- c-remove -

发生了一些错误

-

{{ .error }}

+ +

An error happened

+

{{ .error }}

diff --git a/templates/head.tmpl b/templates/head.tmpl index ae72b1d..9df8e47 100644 --- a/templates/head.tmpl +++ b/templates/head.tmpl @@ -1,10 +1,16 @@ diff --git a/templates/index.tmpl b/templates/index.tmpl index 93e7b19..f206a6a 100644 --- a/templates/index.tmpl +++ b/templates/index.tmpl @@ -1,5 +1,5 @@ {{ define "index.tmpl" }} - + MiSSO {{ template "head.tmpl" }} @@ -24,13 +24,21 @@
- access-key -

欢迎来到 MiSSO

+ +

Welcome to MiSSO

- 直接访问这里好像不太对哦 + You should not access this page directly.

- 查看文档 + Documentation

diff --git a/types/Config.go b/types/Config.go index 99d0e59..9b98f97 100644 --- a/types/Config.go +++ b/types/Config.go @@ -14,4 +14,10 @@ type Config struct { Hydra struct { AdminUrl string `yaml:"admin_url"` } `yaml:"hydra"` + Time struct { + RequestValid int64 `yaml:"request_valid"` + LoginRemember int64 `yaml:"login_remember"` + ConsentRemember int64 `yaml:"consent_remember"` + UserinfoCache int64 `yaml:"userinfo_cache"` + } `yaml:"time"` } diff --git a/types/MisskeyUser.go b/types/MisskeyUser.go index 25a7ca2..61e2d71 100644 --- a/types/MisskeyUser.go +++ b/types/MisskeyUser.go @@ -1,186 +1,8 @@ package types -import "time" - -type MisskeyUser struct { - Id string `json:"id"` - Name string `json:"name"` - Username string `json:"username"` - Host interface{} `json:"host"` - AvatarUrl string `json:"avatarUrl"` - AvatarBlurhash string `json:"avatarBlurhash"` - IsBot bool `json:"isBot"` - IsCat bool `json:"isCat"` - OnlineStatus string `json:"onlineStatus"` - Url interface{} `json:"url"` - Uri interface{} `json:"uri"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` - LastFetchedAt interface{} `json:"lastFetchedAt"` - BannerUrl string `json:"bannerUrl"` - BannerBlurhash string `json:"bannerBlurhash"` - IsLocked bool `json:"isLocked"` - IsSilenced bool `json:"isSilenced"` - IsSuspended bool `json:"isSuspended"` - Description string `json:"description"` - Location string `json:"location"` - Birthday string `json:"birthday"` - Lang string `json:"lang"` - Fields []struct { - Name string `json:"name"` - Value string `json:"value"` - } `json:"fields"` - FollowersCount int `json:"followersCount"` - FollowingCount int `json:"followingCount"` - NotesCount int `json:"notesCount"` - PinnedNoteIds []string `json:"pinnedNoteIds"` - PinnedNotes []struct { - Id string `json:"id"` - CreatedAt time.Time `json:"createdAt"` - UserId string `json:"userId"` - User struct { - Id string `json:"id"` - Name string `json:"name"` - Username string `json:"username"` - Host interface{} `json:"host"` - AvatarUrl string `json:"avatarUrl"` - AvatarBlurhash string `json:"avatarBlurhash"` - IsBot bool `json:"isBot"` - IsCat bool `json:"isCat"` - OnlineStatus string `json:"onlineStatus"` - } `json:"user"` - Text string `json:"text"` - Cw *string `json:"cw"` - Visibility string `json:"visibility"` - LocalOnly bool `json:"localOnly"` - RenoteCount int `json:"renoteCount"` - RepliesCount int `json:"repliesCount"` - Reactions map[string]int `json:"reactions"` - FileIds []string `json:"fileIds"` - Files []struct { - Id string `json:"id"` - CreatedAt time.Time `json:"createdAt"` - Name string `json:"name"` - Type string `json:"type"` - Md5 string `json:"md5"` - Size int `json:"size"` - IsSensitive bool `json:"isSensitive"` - Blurhash string `json:"blurhash"` - Properties struct { - Width int `json:"width"` - Height int `json:"height"` - } `json:"properties"` - Url string `json:"url"` - ThumbnailUrl string `json:"thumbnailUrl"` - Comment interface{} `json:"comment"` - FolderId interface{} `json:"folderId"` - Folder interface{} `json:"folder"` - UserId interface{} `json:"userId"` - User interface{} `json:"user"` - } `json:"files"` - ReplyId interface{} `json:"replyId"` - RenoteId interface{} `json:"renoteId"` - MyReaction string `json:"myReaction,omitempty"` - } `json:"pinnedNotes"` - PinnedPageId string `json:"pinnedPageId"` - PinnedPage struct { - Id string `json:"id"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` - UserId string `json:"userId"` - User struct { - Id string `json:"id"` - Name string `json:"name"` - Username string `json:"username"` - Host interface{} `json:"host"` - AvatarUrl string `json:"avatarUrl"` - AvatarBlurhash string `json:"avatarBlurhash"` - IsBot bool `json:"isBot"` - IsCat bool `json:"isCat"` - OnlineStatus string `json:"onlineStatus"` - } `json:"user"` - Content []struct { - Id string `json:"id"` - Text string `json:"text,omitempty"` - Type string `json:"type"` - Title string `json:"title,omitempty"` - } `json:"content"` - Variables []interface{} `json:"variables"` - Title string `json:"title"` - Name string `json:"name"` - Summary string `json:"summary"` - HideTitleWhenPinned bool `json:"hideTitleWhenPinned"` - AlignCenter bool `json:"alignCenter"` - Font string `json:"font"` - Script string `json:"script"` - EyeCatchingImageId string `json:"eyeCatchingImageId"` - EyeCatchingImage struct { - Id string `json:"id"` - CreatedAt time.Time `json:"createdAt"` - Name string `json:"name"` - Type string `json:"type"` - Md5 string `json:"md5"` - Size int `json:"size"` - IsSensitive bool `json:"isSensitive"` - Blurhash string `json:"blurhash"` - Properties struct { - Width int `json:"width"` - Height int `json:"height"` - } `json:"properties"` - Url string `json:"url"` - ThumbnailUrl string `json:"thumbnailUrl"` - Comment interface{} `json:"comment"` - FolderId interface{} `json:"folderId"` - Folder interface{} `json:"folder"` - UserId interface{} `json:"userId"` - User interface{} `json:"user"` - } `json:"eyeCatchingImage"` - AttachedFiles []interface{} `json:"attachedFiles"` - LikedCount int `json:"likedCount"` - IsLiked bool `json:"isLiked"` - } `json:"pinnedPage"` - PublicReactions bool `json:"publicReactions"` - FfVisibility string `json:"ffVisibility"` - TwoFactorEnabled bool `json:"twoFactorEnabled"` - UsePasswordLessLogin bool `json:"usePasswordLessLogin"` - SecurityKeys bool `json:"securityKeys"` - Roles []struct { - Id string `json:"id"` - Name string `json:"name"` - Color string `json:"color"` - Description string `json:"description"` - IsModerator bool `json:"isModerator"` - IsAdministrator bool `json:"isAdministrator"` - } `json:"roles"` - AvatarId string `json:"avatarId"` - BannerId string `json:"bannerId"` - IsModerator bool `json:"isModerator"` - IsAdmin bool `json:"isAdmin"` - InjectFeaturedNote bool `json:"injectFeaturedNote"` - ReceiveAnnouncementEmail bool `json:"receiveAnnouncementEmail"` - AlwaysMarkNsfw bool `json:"alwaysMarkNsfw"` - AutoSensitive bool `json:"autoSensitive"` - CarefulBot bool `json:"carefulBot"` - AutoAcceptFollowed bool `json:"autoAcceptFollowed"` - NoCrawle bool `json:"noCrawle"` - IsExplorable bool `json:"isExplorable"` - IsDeleted bool `json:"isDeleted"` - HideOnlineStatus bool `json:"hideOnlineStatus"` - HasUnreadSpecifiedNotes bool `json:"hasUnreadSpecifiedNotes"` - HasUnreadMentions bool `json:"hasUnreadMentions"` - HasUnreadAnnouncement bool `json:"hasUnreadAnnouncement"` - HasUnreadAntenna bool `json:"hasUnreadAntenna"` - HasUnreadChannel bool `json:"hasUnreadChannel"` - HasUnreadMessagingMessage bool `json:"hasUnreadMessagingMessage"` - HasUnreadNotification bool `json:"hasUnreadNotification"` - HasPendingReceivedFollowRequest bool `json:"hasPendingReceivedFollowRequest"` - Integrations struct { - } `json:"integrations"` - MutedWords [][]string `json:"mutedWords"` - MutedInstances []string `json:"mutedInstances"` - MutingNotificationTypes []string `json:"mutingNotificationTypes"` - EmailNotificationTypes []string `json:"emailNotificationTypes"` - ShowTimelineReplies bool `json:"showTimelineReplies"` +type MisskeyUserBase struct { + Username string `json:"username"` + // Ignore other fields } -// TODO: Find a better way to split necessary fields and additional fields +type MisskeyUser = map[string]any // Just raw json map diff --git a/utils/get_userinfo.go b/utils/get_userinfo.go index 5d269d1..7c3700c 100644 --- a/utils/get_userinfo.go +++ b/utils/get_userinfo.go @@ -13,7 +13,7 @@ import ( func GetUserinfo(subject string) (*types.MisskeyUser, error) { // Check cache key global.Logger.Debugf("Checking userinfo cache...") - userinfoCacheKey := fmt.Sprintf(consts.REDIS_KEY_SHARE_USER_INFO, subject) + userinfoCacheKey := fmt.Sprintf(consts.REDIS_KEY_USER_INFO, subject) exist, err := global.Redis.Exists(context.Background(), userinfoCacheKey).Result() if err != nil { global.Logger.Errorf("Failed to check userinfo exist status with error: %v", err) @@ -38,7 +38,7 @@ func GetUserinfo(subject string) (*types.MisskeyUser, error) { // Fallback to get info directly, we need user's access token. global.Logger.Debugf("No cached userinfo found (or valid), trying to get latest response.") - accessTokenCacheKey := fmt.Sprintf(consts.REDIS_KEY_SHARE_ACCESS_TOKEN, subject) + accessTokenCacheKey := fmt.Sprintf(consts.REDIS_KEY_USER_ACCESS_TOKEN, subject) accessToken, err := global.Redis.Get(context.Background(), accessTokenCacheKey).Result() if err != nil { global.Logger.Errorf("Failed to get user access token with error: %v", err) @@ -52,6 +52,9 @@ func GetUserinfo(subject string) (*types.MisskeyUser, error) { return nil, err } + // Append subject as email to userinfo + (*userinfo)["email"] = subject + // Save userinfo into redis _ = SaveUserinfo(subject, userinfo) // Ignore errors diff --git a/utils/save_userinfo.go b/utils/save_userinfo.go index d77819c..9c25e03 100644 --- a/utils/save_userinfo.go +++ b/utils/save_userinfo.go @@ -4,9 +4,11 @@ import ( "context" "encoding/json" "fmt" + "misso/config" "misso/consts" "misso/global" "misso/types" + "time" ) func SaveUserinfo(subject string, userinfo *types.MisskeyUser) error { @@ -15,8 +17,8 @@ func SaveUserinfo(subject string, userinfo *types.MisskeyUser) error { global.Logger.Errorf("Failed to parse accept context with error: %v", err) return err } - sessUserInfoKey := fmt.Sprintf(consts.REDIS_KEY_SHARE_USER_INFO, subject) - err = global.Redis.Set(context.Background(), sessUserInfoKey, userinfoBytes, consts.TIME_USERINFO_CACHE).Err() + sessUserInfoKey := fmt.Sprintf(consts.REDIS_KEY_USER_INFO, subject) + err = global.Redis.Set(context.Background(), sessUserInfoKey, userinfoBytes, time.Duration(config.Config.Time.UserinfoCache)*time.Second).Err() if err != nil { global.Logger.Errorf("Failed to save session user info into redis with error: %v", err) return err