Live video on the AT Protocol

oauth: add downstream authorization code

+42 -25
+1 -1
pkg/api/oauth.go
··· 262 q := u.Query() 263 q.Set("iss", "https://longos.iameli.link") 264 q.Set("state", upstreamSession.DownstreamPAR.State) 265 - q.Set("code", "asdf") 266 u.RawQuery = q.Encode() 267 http.Redirect(w, r, u.String(), http.StatusTemporaryRedirect) 268 }
··· 262 q := u.Query() 263 q.Set("iss", "https://longos.iameli.link") 264 q.Set("state", upstreamSession.DownstreamPAR.State) 265 + q.Set("code", upstreamSession.DownstreamAuthorizationCode) 266 u.RawQuery = q.Encode() 267 http.Redirect(w, r, u.String(), http.StatusTemporaryRedirect) 268 }
+14 -2
pkg/atproto/oauth_downstream.go
··· 54 return nil, fmt.Errorf("could not get oauth session: %w", err) 55 } 56 57 accessToken, err := generateJWT(cli, par.JKT, session.RepoDID) 58 if err != nil { 59 return nil, fmt.Errorf("could not generate access token: %w", err) 60 } 61 62 - refreshToken, err := generateRefreshToken(cli) 63 if err != nil { 64 return nil, fmt.Errorf("could not generate refresh token: %w", err) 65 } ··· 140 return tokenString, nil 141 } 142 143 - func generateRefreshToken(cli *config.CLI) (string, error) { 144 uu, err := uuid.NewV7() 145 if err != nil { 146 return "", err 147 } 148 return fmt.Sprintf("refresh-%s", uu.String()), nil 149 }
··· 54 return nil, fmt.Errorf("could not get oauth session: %w", err) 55 } 56 57 + if session.DownstreamAuthorizationCode != tokenRequest.Code { 58 + return nil, fmt.Errorf("invalid authorization code") 59 + } 60 + 61 accessToken, err := generateJWT(cli, par.JKT, session.RepoDID) 62 if err != nil { 63 return nil, fmt.Errorf("could not generate access token: %w", err) 64 } 65 66 + refreshToken, err := generateRefreshToken() 67 if err != nil { 68 return nil, fmt.Errorf("could not generate refresh token: %w", err) 69 } ··· 144 return tokenString, nil 145 } 146 147 + func generateRefreshToken() (string, error) { 148 uu, err := uuid.NewV7() 149 if err != nil { 150 return "", err 151 } 152 return fmt.Sprintf("refresh-%s", uu.String()), nil 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 return nil, fmt.Errorf("scope mismatch: %s != %s", itResp.Scope, meta.Scope) 137 } 138 139 expiry := now.Add(time.Second * time.Duration(itResp.ExpiresIn)).UTC() 140 session.UpstreamAccessToken = itResp.AccessToken 141 session.UpstreamAccessTokenExp = expiry 142 session.UpstreamRefreshToken = itResp.RefreshToken 143 - 144 - log.Log(ctx, "itResp", "itResp", itResp) 145 146 authArgs := &oauth.XrpcAuthedRequestArgs{ 147 Did: session.RepoDID,
··· 136 return nil, fmt.Errorf("scope mismatch: %s != %s", itResp.Scope, meta.Scope) 137 } 138 139 + downstreamCode, err := generateAuthorizationCode() 140 + if err != nil { 141 + return nil, fmt.Errorf("failed to generate downstream code: %w", err) 142 + } 143 + 144 expiry := now.Add(time.Second * time.Duration(itResp.ExpiresIn)).UTC() 145 session.UpstreamAccessToken = itResp.AccessToken 146 session.UpstreamAccessTokenExp = expiry 147 session.UpstreamRefreshToken = itResp.RefreshToken 148 + session.DownstreamAuthorizationCode = downstreamCode 149 150 authArgs := &oauth.XrpcAuthedRequestArgs{ 151 Did: session.RepoDID,
+21 -20
pkg/model/oauth_session.go
··· 9 10 // OAuthSession stores authentication data needed during the OAuth flow 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"` 32 } 33 34 func (o *OAuthSession) TableName() string {
··· 9 10 // OAuthSession stores authentication data needed during the OAuth flow 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 + DownstreamAuthorizationCode string `gorm:"column:downstream_authorization_code;index"` 30 + CreatedAt time.Time 31 + UpdatedAt time.Time 32 + DeletedAt gorm.DeletedAt `gorm:"index"` 33 } 34 35 func (o *OAuthSession) TableName() string {