oauth2: correctly populate token expiry data

* Populate the field when needed
* Convert it to a time instead of a duration
This commit is contained in:
Simone Gotti 2019-04-29 14:57:07 +02:00
parent 52bcf096c7
commit 95e73e66a0
4 changed files with 169 additions and 145 deletions

View File

@ -18,6 +18,7 @@ import (
"encoding/json" "encoding/json"
"net/http" "net/http"
"strconv" "strconv"
"time"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sorintlab/agola/internal/db" "github.com/sorintlab/agola/internal/db"
@ -133,9 +134,10 @@ func (h *CreateUserHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
RemoteSourceName: req.CreateUserLARequest.RemoteSourceName, RemoteSourceName: req.CreateUserLARequest.RemoteSourceName,
RemoteUserID: req.CreateUserLARequest.RemoteUserID, RemoteUserID: req.CreateUserLARequest.RemoteUserID,
RemoteUserName: req.CreateUserLARequest.RemoteUserName, RemoteUserName: req.CreateUserLARequest.RemoteUserName,
UserAccessToken: req.CreateUserLARequest.UserAccessToken,
Oauth2AccessToken: req.CreateUserLARequest.Oauth2AccessToken, Oauth2AccessToken: req.CreateUserLARequest.Oauth2AccessToken,
Oauth2RefreshToken: req.CreateUserLARequest.Oauth2RefreshToken, Oauth2RefreshToken: req.CreateUserLARequest.Oauth2RefreshToken,
UserAccessToken: req.CreateUserLARequest.UserAccessToken, Oauth2AccessTokenExpiresAt: req.CreateUserLARequest.Oauth2AccessTokenExpiresAt,
} }
} }
@ -305,6 +307,7 @@ type CreateUserLARequest struct {
UserAccessToken string `json:"user_access_token"` UserAccessToken string `json:"user_access_token"`
Oauth2AccessToken string `json:"oauth2_access_token"` Oauth2AccessToken string `json:"oauth2_access_token"`
Oauth2RefreshToken string `json:"oauth2_refresh_token"` Oauth2RefreshToken string `json:"oauth2_refresh_token"`
Oauth2AccessTokenExpiresAt time.Time `json:"oauth_2_access_token_expires_at"`
} }
type CreateUserLAHandler struct { type CreateUserLAHandler struct {
@ -333,9 +336,10 @@ func (h *CreateUserLAHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
RemoteSourceName: req.RemoteSourceName, RemoteSourceName: req.RemoteSourceName,
RemoteUserID: req.RemoteUserID, RemoteUserID: req.RemoteUserID,
RemoteUserName: req.RemoteUserName, RemoteUserName: req.RemoteUserName,
UserAccessToken: req.UserAccessToken,
Oauth2AccessToken: req.Oauth2AccessToken, Oauth2AccessToken: req.Oauth2AccessToken,
Oauth2RefreshToken: req.Oauth2RefreshToken, Oauth2RefreshToken: req.Oauth2RefreshToken,
UserAccessToken: req.UserAccessToken, Oauth2AccessTokenExpiresAt: req.Oauth2AccessTokenExpiresAt,
} }
user, err := h.ch.CreateUserLA(ctx, creq) user, err := h.ch.CreateUserLA(ctx, creq)
if httpError(w, err) { if httpError(w, err) {
@ -378,6 +382,7 @@ type UpdateUserLARequest struct {
UserAccessToken string `json:"user_access_token"` UserAccessToken string `json:"user_access_token"`
Oauth2AccessToken string `json:"oauth2_access_token"` Oauth2AccessToken string `json:"oauth2_access_token"`
Oauth2RefreshToken string `json:"oauth2_refresh_token"` Oauth2RefreshToken string `json:"oauth2_refresh_token"`
Oauth2AccessTokenExpiresAt time.Time `json:"oauth_2_access_token_expires_at"`
} }
type UpdateUserLAHandler struct { type UpdateUserLAHandler struct {
@ -407,9 +412,10 @@ func (h *UpdateUserLAHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
LinkedAccountID: linkedAccountID, LinkedAccountID: linkedAccountID,
RemoteUserID: req.RemoteUserID, RemoteUserID: req.RemoteUserID,
RemoteUserName: req.RemoteUserName, RemoteUserName: req.RemoteUserName,
UserAccessToken: req.UserAccessToken,
Oauth2AccessToken: req.Oauth2AccessToken, Oauth2AccessToken: req.Oauth2AccessToken,
Oauth2RefreshToken: req.Oauth2RefreshToken, Oauth2RefreshToken: req.Oauth2RefreshToken,
UserAccessToken: req.UserAccessToken, Oauth2AccessTokenExpiresAt: req.Oauth2AccessTokenExpiresAt,
} }
user, err := h.ch.UpdateUserLA(ctx, creq) user, err := h.ch.UpdateUserLA(ctx, creq)
if httpError(w, err) { if httpError(w, err) {

View File

@ -18,6 +18,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"path" "path"
"time"
"github.com/sorintlab/agola/internal/datamanager" "github.com/sorintlab/agola/internal/datamanager"
"github.com/sorintlab/agola/internal/db" "github.com/sorintlab/agola/internal/db"
@ -316,6 +317,7 @@ func (s *CommandHandler) CreateUser(ctx context.Context, req *CreateUserRequest)
UserAccessToken: req.CreateUserLARequest.UserAccessToken, UserAccessToken: req.CreateUserLARequest.UserAccessToken,
Oauth2AccessToken: req.CreateUserLARequest.Oauth2AccessToken, Oauth2AccessToken: req.CreateUserLARequest.Oauth2AccessToken,
Oauth2RefreshToken: req.CreateUserLARequest.Oauth2RefreshToken, Oauth2RefreshToken: req.CreateUserLARequest.Oauth2RefreshToken,
Oauth2AccessTokenExpiresAt: req.CreateUserLARequest.Oauth2AccessTokenExpiresAt,
} }
user.LinkedAccounts[la.ID] = la user.LinkedAccounts[la.ID] = la
@ -407,6 +409,7 @@ type CreateUserLARequest struct {
UserAccessToken string UserAccessToken string
Oauth2AccessToken string Oauth2AccessToken string
Oauth2RefreshToken string Oauth2RefreshToken string
Oauth2AccessTokenExpiresAt time.Time
} }
func (s *CommandHandler) CreateUserLA(ctx context.Context, req *CreateUserLARequest) (*types.LinkedAccount, error) { func (s *CommandHandler) CreateUserLA(ctx context.Context, req *CreateUserLARequest) (*types.LinkedAccount, error) {
@ -473,6 +476,7 @@ func (s *CommandHandler) CreateUserLA(ctx context.Context, req *CreateUserLARequ
UserAccessToken: req.UserAccessToken, UserAccessToken: req.UserAccessToken,
Oauth2AccessToken: req.Oauth2AccessToken, Oauth2AccessToken: req.Oauth2AccessToken,
Oauth2RefreshToken: req.Oauth2RefreshToken, Oauth2RefreshToken: req.Oauth2RefreshToken,
Oauth2AccessTokenExpiresAt: req.Oauth2AccessTokenExpiresAt,
} }
user.LinkedAccounts[la.ID] = la user.LinkedAccounts[la.ID] = la
@ -562,6 +566,7 @@ type UpdateUserLARequest struct {
UserAccessToken string UserAccessToken string
Oauth2AccessToken string Oauth2AccessToken string
Oauth2RefreshToken string Oauth2RefreshToken string
Oauth2AccessTokenExpiresAt time.Time
} }
func (s *CommandHandler) UpdateUserLA(ctx context.Context, req *UpdateUserLARequest) (*types.LinkedAccount, error) { func (s *CommandHandler) UpdateUserLA(ctx context.Context, req *UpdateUserLARequest) (*types.LinkedAccount, error) {
@ -617,6 +622,7 @@ func (s *CommandHandler) UpdateUserLA(ctx context.Context, req *UpdateUserLARequ
la.UserAccessToken = req.UserAccessToken la.UserAccessToken = req.UserAccessToken
la.Oauth2AccessToken = req.Oauth2AccessToken la.Oauth2AccessToken = req.Oauth2AccessToken
la.Oauth2RefreshToken = req.Oauth2RefreshToken la.Oauth2RefreshToken = req.Oauth2RefreshToken
la.Oauth2AccessTokenExpiresAt = req.Oauth2AccessTokenExpiresAt
userj, err := json.Marshal(user) userj, err := json.Marshal(user)
if err != nil { if err != nil {

View File

@ -17,6 +17,7 @@ package command
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"time"
gitsource "github.com/sorintlab/agola/internal/gitsources" gitsource "github.com/sorintlab/agola/internal/gitsources"
csapi "github.com/sorintlab/agola/internal/services/configstore/api" csapi "github.com/sorintlab/agola/internal/services/configstore/api"
@ -102,9 +103,10 @@ func (c *CommandHandler) CreateUserToken(ctx context.Context, req *CreateUserTok
type CreateUserLARequest struct { type CreateUserLARequest struct {
UserName string UserName string
RemoteSourceName string RemoteSourceName string
RemoteSourceUserAccessToken string UserAccessToken string
RemoteSourceOauth2AccessToken string Oauth2AccessToken string
RemoteSourceOauth2RefreshToken string Oauth2RefreshToken string
Oauth2AccessTokenExpiresAt time.Time
} }
func (c *CommandHandler) CreateUserLA(ctx context.Context, req *CreateUserLARequest) (*types.LinkedAccount, error) { func (c *CommandHandler) CreateUserLA(ctx context.Context, req *CreateUserLARequest) (*types.LinkedAccount, error) {
@ -130,7 +132,7 @@ func (c *CommandHandler) CreateUserLA(ctx context.Context, req *CreateUserLARequ
return nil, util.NewErrBadRequest(errors.Errorf("user %q already have a linked account for remote source %q", userName, rs.Name)) return nil, util.NewErrBadRequest(errors.Errorf("user %q already have a linked account for remote source %q", userName, rs.Name))
} }
accessToken, err := common.GetAccessToken(rs.AuthType, req.RemoteSourceUserAccessToken, req.RemoteSourceOauth2AccessToken) accessToken, err := common.GetAccessToken(rs.AuthType, req.UserAccessToken, req.Oauth2AccessToken)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -151,9 +153,10 @@ func (c *CommandHandler) CreateUserLA(ctx context.Context, req *CreateUserLARequ
RemoteSourceName: req.RemoteSourceName, RemoteSourceName: req.RemoteSourceName,
RemoteUserID: remoteUserInfo.ID, RemoteUserID: remoteUserInfo.ID,
RemoteUserName: remoteUserInfo.LoginName, RemoteUserName: remoteUserInfo.LoginName,
Oauth2AccessToken: req.RemoteSourceOauth2AccessToken, UserAccessToken: req.UserAccessToken,
Oauth2RefreshToken: req.RemoteSourceOauth2RefreshToken, Oauth2AccessToken: req.Oauth2AccessToken,
UserAccessToken: req.RemoteSourceUserAccessToken, Oauth2RefreshToken: req.Oauth2RefreshToken,
Oauth2AccessTokenExpiresAt: req.Oauth2AccessTokenExpiresAt,
} }
c.log.Infof("creating linked account") c.log.Infof("creating linked account")
@ -169,9 +172,10 @@ func (c *CommandHandler) CreateUserLA(ctx context.Context, req *CreateUserLARequ
type RegisterUserRequest struct { type RegisterUserRequest struct {
UserName string UserName string
RemoteSourceName string RemoteSourceName string
RemoteSourceUserAccessToken string UserAccessToken string
RemoteSourceOauth2AccessToken string Oauth2AccessToken string
RemoteSourceOauth2RefreshToken string Oauth2RefreshToken string
Oauth2AccessTokenExpiresAt time.Time
} }
func (c *CommandHandler) RegisterUser(ctx context.Context, req *RegisterUserRequest) (*types.User, error) { func (c *CommandHandler) RegisterUser(ctx context.Context, req *RegisterUserRequest) (*types.User, error) {
@ -188,7 +192,7 @@ func (c *CommandHandler) RegisterUser(ctx context.Context, req *RegisterUserRequ
} }
c.log.Infof("rs: %s", util.Dump(rs)) c.log.Infof("rs: %s", util.Dump(rs))
accessToken, err := common.GetAccessToken(rs.AuthType, req.RemoteSourceUserAccessToken, req.RemoteSourceOauth2AccessToken) accessToken, err := common.GetAccessToken(rs.AuthType, req.UserAccessToken, req.Oauth2AccessToken)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -211,9 +215,10 @@ func (c *CommandHandler) RegisterUser(ctx context.Context, req *RegisterUserRequ
RemoteSourceName: req.RemoteSourceName, RemoteSourceName: req.RemoteSourceName,
RemoteUserID: remoteUserInfo.ID, RemoteUserID: remoteUserInfo.ID,
RemoteUserName: remoteUserInfo.LoginName, RemoteUserName: remoteUserInfo.LoginName,
Oauth2AccessToken: req.RemoteSourceOauth2AccessToken, UserAccessToken: req.UserAccessToken,
Oauth2RefreshToken: req.RemoteSourceOauth2RefreshToken, Oauth2AccessToken: req.Oauth2AccessToken,
UserAccessToken: req.RemoteSourceUserAccessToken, Oauth2RefreshToken: req.Oauth2RefreshToken,
Oauth2AccessTokenExpiresAt: req.Oauth2AccessTokenExpiresAt,
}, },
} }
@ -229,9 +234,10 @@ func (c *CommandHandler) RegisterUser(ctx context.Context, req *RegisterUserRequ
type LoginUserRequest struct { type LoginUserRequest struct {
RemoteSourceName string RemoteSourceName string
RemoteSourceUserAccessToken string UserAccessToken string
RemoteSourceOauth2AccessToken string Oauth2AccessToken string
RemoteSourceOauth2RefreshToken string Oauth2RefreshToken string
Oauth2AccessTokenExpiresAt time.Time
} }
type LoginUserResponse struct { type LoginUserResponse struct {
@ -246,7 +252,7 @@ func (c *CommandHandler) LoginUser(ctx context.Context, req *LoginUserRequest) (
} }
c.log.Infof("rs: %s", util.Dump(rs)) c.log.Infof("rs: %s", util.Dump(rs))
accessToken, err := common.GetAccessToken(rs.AuthType, req.RemoteSourceUserAccessToken, req.RemoteSourceOauth2AccessToken) accessToken, err := common.GetAccessToken(rs.AuthType, req.UserAccessToken, req.Oauth2AccessToken)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -281,20 +287,21 @@ func (c *CommandHandler) LoginUser(ctx context.Context, req *LoginUserRequest) (
} }
// Update oauth tokens if they have changed since the getuserinfo request may have updated them // Update oauth tokens if they have changed since the getuserinfo request may have updated them
if la.Oauth2AccessToken != req.RemoteSourceOauth2AccessToken || if la.Oauth2AccessToken != req.Oauth2AccessToken ||
la.Oauth2RefreshToken != req.RemoteSourceOauth2RefreshToken || la.Oauth2RefreshToken != req.Oauth2RefreshToken ||
la.UserAccessToken != req.RemoteSourceUserAccessToken { la.UserAccessToken != req.UserAccessToken {
la.Oauth2AccessToken = req.RemoteSourceOauth2AccessToken la.Oauth2AccessToken = req.Oauth2AccessToken
la.Oauth2RefreshToken = req.RemoteSourceOauth2RefreshToken la.Oauth2RefreshToken = req.Oauth2RefreshToken
la.UserAccessToken = req.RemoteSourceUserAccessToken la.UserAccessToken = req.UserAccessToken
creq := &csapi.UpdateUserLARequest{ creq := &csapi.UpdateUserLARequest{
RemoteUserID: la.RemoteUserID, RemoteUserID: la.RemoteUserID,
RemoteUserName: la.RemoteUserName, RemoteUserName: la.RemoteUserName,
UserAccessToken: la.UserAccessToken,
Oauth2AccessToken: la.Oauth2AccessToken, Oauth2AccessToken: la.Oauth2AccessToken,
Oauth2RefreshToken: la.Oauth2RefreshToken, Oauth2RefreshToken: la.Oauth2RefreshToken,
UserAccessToken: la.UserAccessToken, Oauth2AccessTokenExpiresAt: la.Oauth2AccessTokenExpiresAt,
} }
c.log.Infof("updating user %q linked account", user.UserName) c.log.Infof("updating user %q linked account", user.UserName)
@ -318,9 +325,10 @@ func (c *CommandHandler) LoginUser(ctx context.Context, req *LoginUserRequest) (
type AuthorizeRequest struct { type AuthorizeRequest struct {
RemoteSourceName string RemoteSourceName string
RemoteSourceUserAccessToken string UserAccessToken string
RemoteSourceOauth2AccessToken string Oauth2AccessToken string
RemoteSourceOauth2RefreshToken string Oauth2RefreshToken string
Oauth2AccessTokenExpiresAt time.Time
} }
type AuthorizeResponse struct { type AuthorizeResponse struct {
@ -335,7 +343,7 @@ func (c *CommandHandler) Authorize(ctx context.Context, req *AuthorizeRequest) (
} }
c.log.Infof("rs: %s", util.Dump(rs)) c.log.Infof("rs: %s", util.Dump(rs))
accessToken, err := common.GetAccessToken(rs.AuthType, req.RemoteSourceUserAccessToken, req.RemoteSourceOauth2AccessToken) accessToken, err := common.GetAccessToken(rs.AuthType, req.UserAccessToken, req.Oauth2AccessToken)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -433,7 +441,7 @@ func (c *CommandHandler) HandleRemoteSourceAuth(ctx context.Context, remoteSourc
if err != nil { if err != nil {
return nil, err return nil, err
} }
cres, err := c.HandleRemoteSourceAuthRequest(ctx, requestType, string(requestj), accessToken, "", "") cres, err := c.HandleRemoteSourceAuthRequest(ctx, requestType, string(requestj), accessToken, "", "", time.Time{})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -464,7 +472,7 @@ type CreateUserLAResponse struct {
LinkedAccount *types.LinkedAccount LinkedAccount *types.LinkedAccount
} }
func (c *CommandHandler) HandleRemoteSourceAuthRequest(ctx context.Context, requestType RemoteSourceRequestType, requestString string, userAccessToken, Oauth2AccessToken, Oauth2RefreshToken string) (*RemoteSourceAuthResult, error) { func (c *CommandHandler) HandleRemoteSourceAuthRequest(ctx context.Context, requestType RemoteSourceRequestType, requestString string, userAccessToken, oauth2AccessToken, oauth2RefreshToken string, oauth2AccessTokenExpiresAt time.Time) (*RemoteSourceAuthResult, error) {
switch requestType { switch requestType {
case RemoteSourceRequestTypeCreateUserLA: case RemoteSourceRequestTypeCreateUserLA:
var req *CreateUserLARequest var req *CreateUserLARequest
@ -475,9 +483,10 @@ func (c *CommandHandler) HandleRemoteSourceAuthRequest(ctx context.Context, requ
creq := &CreateUserLARequest{ creq := &CreateUserLARequest{
UserName: req.UserName, UserName: req.UserName,
RemoteSourceName: req.RemoteSourceName, RemoteSourceName: req.RemoteSourceName,
RemoteSourceUserAccessToken: userAccessToken, UserAccessToken: userAccessToken,
RemoteSourceOauth2AccessToken: Oauth2AccessToken, Oauth2AccessToken: oauth2AccessToken,
RemoteSourceOauth2RefreshToken: Oauth2RefreshToken, Oauth2RefreshToken: oauth2RefreshToken,
Oauth2AccessTokenExpiresAt: oauth2AccessTokenExpiresAt,
} }
la, err := c.CreateUserLA(ctx, creq) la, err := c.CreateUserLA(ctx, creq)
if err != nil { if err != nil {
@ -490,6 +499,29 @@ func (c *CommandHandler) HandleRemoteSourceAuthRequest(ctx context.Context, requ
}, },
}, nil }, nil
case RemoteSourceRequestTypeRegisterUser:
var req *RegisterUserRequest
if err := json.Unmarshal([]byte(requestString), &req); err != nil {
return nil, errors.Errorf("failed to unmarshal request")
}
creq := &RegisterUserRequest{
UserName: req.UserName,
RemoteSourceName: req.RemoteSourceName,
UserAccessToken: userAccessToken,
Oauth2AccessToken: oauth2AccessToken,
Oauth2RefreshToken: oauth2RefreshToken,
Oauth2AccessTokenExpiresAt: oauth2AccessTokenExpiresAt,
}
cresp, err := c.RegisterUser(ctx, creq)
if err != nil {
return nil, err
}
return &RemoteSourceAuthResult{
RequestType: requestType,
Response: cresp,
}, nil
case RemoteSourceRequestTypeLoginUser: case RemoteSourceRequestTypeLoginUser:
var req *LoginUserRequest var req *LoginUserRequest
if err := json.Unmarshal([]byte(requestString), &req); err != nil { if err := json.Unmarshal([]byte(requestString), &req); err != nil {
@ -498,9 +530,10 @@ func (c *CommandHandler) HandleRemoteSourceAuthRequest(ctx context.Context, requ
creq := &LoginUserRequest{ creq := &LoginUserRequest{
RemoteSourceName: req.RemoteSourceName, RemoteSourceName: req.RemoteSourceName,
RemoteSourceUserAccessToken: userAccessToken, UserAccessToken: userAccessToken,
RemoteSourceOauth2AccessToken: Oauth2AccessToken, Oauth2AccessToken: oauth2AccessToken,
RemoteSourceOauth2RefreshToken: Oauth2RefreshToken, Oauth2RefreshToken: oauth2RefreshToken,
Oauth2AccessTokenExpiresAt: oauth2AccessTokenExpiresAt,
} }
cresp, err := c.LoginUser(ctx, creq) cresp, err := c.LoginUser(ctx, creq)
if err != nil { if err != nil {
@ -519,9 +552,10 @@ func (c *CommandHandler) HandleRemoteSourceAuthRequest(ctx context.Context, requ
creq := &AuthorizeRequest{ creq := &AuthorizeRequest{
RemoteSourceName: req.RemoteSourceName, RemoteSourceName: req.RemoteSourceName,
RemoteSourceUserAccessToken: userAccessToken, UserAccessToken: userAccessToken,
RemoteSourceOauth2AccessToken: Oauth2AccessToken, Oauth2AccessToken: oauth2AccessToken,
RemoteSourceOauth2RefreshToken: Oauth2RefreshToken, Oauth2RefreshToken: oauth2RefreshToken,
Oauth2AccessTokenExpiresAt: oauth2AccessTokenExpiresAt,
} }
cresp, err := c.Authorize(ctx, creq) cresp, err := c.Authorize(ctx, creq)
if err != nil { if err != nil {
@ -532,28 +566,6 @@ func (c *CommandHandler) HandleRemoteSourceAuthRequest(ctx context.Context, requ
Response: cresp, Response: cresp,
}, nil }, nil
case RemoteSourceRequestTypeRegisterUser:
var req *RegisterUserRequest
if err := json.Unmarshal([]byte(requestString), &req); err != nil {
return nil, errors.Errorf("failed to unmarshal request")
}
creq := &RegisterUserRequest{
UserName: req.UserName,
RemoteSourceName: req.RemoteSourceName,
RemoteSourceUserAccessToken: userAccessToken,
RemoteSourceOauth2AccessToken: Oauth2AccessToken,
RemoteSourceOauth2RefreshToken: Oauth2RefreshToken,
}
cresp, err := c.RegisterUser(ctx, creq)
if err != nil {
return nil, err
}
return &RemoteSourceAuthResult{
RequestType: requestType,
Response: cresp,
}, nil
default: default:
return nil, errors.Errorf("unknown request") return nil, errors.Errorf("unknown request")
} }
@ -603,5 +615,5 @@ func (c *CommandHandler) HandleOauth2Callback(ctx context.Context, code, state s
return nil, err return nil, err
} }
return c.HandleRemoteSourceAuthRequest(ctx, requestType, requestString, "", oauth2Token.AccessToken, oauth2Token.RefreshToken) return c.HandleRemoteSourceAuthRequest(ctx, requestType, requestString, "", oauth2Token.AccessToken, oauth2Token.RefreshToken, oauth2Token.Expiry)
} }

View File

@ -129,7 +129,7 @@ type LinkedAccount struct {
Oauth2AccessToken string `json:"oauth2_access_token,omitempty"` Oauth2AccessToken string `json:"oauth2_access_token,omitempty"`
Oauth2RefreshToken string `json:"oauth2_refresh_token,omitempty"` Oauth2RefreshToken string `json:"oauth2_refresh_token,omitempty"`
Oauth2Expire time.Duration `json:"oauth2_expire,omitempty"` Oauth2AccessTokenExpiresAt time.Time `json:"oauth_2_access_token_expires_at,omitempty"`
} }
type Project struct { type Project struct {