Live video on the AT Protocol

oauth: add downstream authorization code

+42 -25
+1 -1
pkg/api/oauth.go
··· 262 262 q := u.Query() 263 263 q.Set("iss", "https://longos.iameli.link") 264 264 q.Set("state", upstreamSession.DownstreamPAR.State) 265 - q.Set("code", "asdf") 265 + q.Set("code", upstreamSession.DownstreamAuthorizationCode) 266 266 u.RawQuery = q.Encode() 267 267 http.Redirect(w, r, u.String(), http.StatusTemporaryRedirect) 268 268 }
+14 -2
pkg/atproto/oauth_downstream.go
··· 54 54 return nil, fmt.Errorf("could not get oauth session: %w", err) 55 55 } 56 56 57 + if session.DownstreamAuthorizationCode != tokenRequest.Code { 58 + return nil, fmt.Errorf("invalid authorization code") 59 + } 60 + 57 61 accessToken, err := generateJWT(cli, par.JKT, session.RepoDID) 58 62 if err != nil { 59 63 return nil, fmt.Errorf("could not generate access token: %w", err) 60 64 } 61 65 62 - refreshToken, err := generateRefreshToken(cli) 66 + refreshToken, err := generateRefreshToken() 63 67 if err != nil { 64 68 return nil, fmt.Errorf("could not generate refresh token: %w", err) 65 69 } ··· 140 144 return tokenString, nil 141 145 } 142 146 143 - func generateRefreshToken(cli *config.CLI) (string, error) { 147 + func generateRefreshToken() (string, error) { 144 148 uu, err := uuid.NewV7() 145 149 if err != nil { 146 150 return "", err 147 151 } 148 152 return fmt.Sprintf("refresh-%s", uu.String()), nil 149 153 } 154 + 155 + func generateAuthorizationCode() (string, error) { 156 + uu, err := uuid.NewV7() 157 + if err != nil { 158 + return "", err 159 + } 160 + return fmt.Sprintf("code-%s", uu.String()), nil 161 + }
+6 -2
pkg/atproto/oauth_upstream.go
··· 136 136 return nil, fmt.Errorf("scope mismatch: %s != %s", itResp.Scope, meta.Scope) 137 137 } 138 138 139 + downstreamCode, err := generateAuthorizationCode() 140 + if err != nil { 141 + return nil, fmt.Errorf("failed to generate downstream code: %w", err) 142 + } 143 + 139 144 expiry := now.Add(time.Second * time.Duration(itResp.ExpiresIn)).UTC() 140 145 session.UpstreamAccessToken = itResp.AccessToken 141 146 session.UpstreamAccessTokenExp = expiry 142 147 session.UpstreamRefreshToken = itResp.RefreshToken 143 - 144 - log.Log(ctx, "itResp", "itResp", itResp) 148 + session.DownstreamAuthorizationCode = downstreamCode 145 149 146 150 authArgs := &oauth.XrpcAuthedRequestArgs{ 147 151 Did: session.RepoDID,
+21 -20
pkg/model/oauth_session.go
··· 9 9 10 10 // OAuthSession stores authentication data needed during the OAuth flow 11 11 type OAuthSession struct { 12 - ID string `gorm:"primarykey"` 13 - RepoDID string `gorm:"column:repo_did;index"` 14 - PDSUrl string `gorm:"column:pds_url"` 15 - UpstreamState string `gorm:"column:upstream_state;index"` 16 - UpstreamAuthServerIssuer string `gorm:"column:upstream_auth_server_issuer"` 17 - UpstreamPKCEVerifier string `gorm:"column:upstream_pkce_verifier"` 18 - UpstreamDPoPNonce string `gorm:"column:upstream_dpop_nonce"` 19 - UpstreamDPoPPrivateJWK []byte `gorm:"column:upstream_dpop_private_jwk;type:text"` 20 - UpstreamAccessToken string `gorm:"column:upstream_access_token"` 21 - UpstreamAccessTokenExp time.Time `gorm:"column:upstream_access_token_exp"` 22 - UpstreamRefreshToken string `gorm:"column:upstream_refresh_token"` 23 - DownstreamPARID string `gorm:"column:downstream_par_id;uniqueIndex"` 24 - DownstreamPAR *PAR `gorm:"foreignKey:DownstreamPARID"` 25 - DownstreamDPoPNonce string `gorm:"column:downstream_dpop_nonce"` 26 - DownstreamDPoPJKT string `gorm:"column:downstream_dpop_jkt"` 27 - DownstreamAccessToken string `gorm:"column:downstream_access_token;index"` 28 - DownstreamRefreshToken string `gorm:"column:downstream_refresh_token;index"` 29 - CreatedAt time.Time 30 - UpdatedAt time.Time 31 - DeletedAt gorm.DeletedAt `gorm:"index"` 12 + ID string `gorm:"primarykey"` 13 + RepoDID string `gorm:"column:repo_did;index"` 14 + PDSUrl string `gorm:"column:pds_url"` 15 + UpstreamState string `gorm:"column:upstream_state;index"` 16 + UpstreamAuthServerIssuer string `gorm:"column:upstream_auth_server_issuer"` 17 + UpstreamPKCEVerifier string `gorm:"column:upstream_pkce_verifier"` 18 + UpstreamDPoPNonce string `gorm:"column:upstream_dpop_nonce"` 19 + UpstreamDPoPPrivateJWK []byte `gorm:"column:upstream_dpop_private_jwk;type:text"` 20 + UpstreamAccessToken string `gorm:"column:upstream_access_token"` 21 + UpstreamAccessTokenExp time.Time `gorm:"column:upstream_access_token_exp"` 22 + UpstreamRefreshToken string `gorm:"column:upstream_refresh_token"` 23 + DownstreamPARID string `gorm:"column:downstream_par_id;uniqueIndex"` 24 + DownstreamPAR *PAR `gorm:"foreignKey:DownstreamPARID"` 25 + DownstreamDPoPNonce string `gorm:"column:downstream_dpop_nonce"` 26 + DownstreamDPoPJKT string `gorm:"column:downstream_dpop_jkt"` 27 + DownstreamAccessToken string `gorm:"column:downstream_access_token;index"` 28 + DownstreamRefreshToken string `gorm:"column:downstream_refresh_token;index"` 29 + DownstreamAuthorizationCode string `gorm:"column:downstream_authorization_code;index"` 30 + CreatedAt time.Time 31 + UpdatedAt time.Time 32 + DeletedAt gorm.DeletedAt `gorm:"index"` 32 33 } 33 34 34 35 func (o *OAuthSession) TableName() string {