tangled
alpha
login
or
join now
stream.place
/
streamplace
Live video on the AT Protocol
74
fork
atom
overview
issues
1
pulls
pipelines
oauth: add downstream authorization code
Eli Mallon
9 months ago
796f82dd
3c97efcb
+42
-25
4 changed files
expand all
collapse all
unified
split
pkg
api
oauth.go
atproto
oauth_downstream.go
oauth_upstream.go
model
oauth_session.go
+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
0
0
0
0
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
}
0
0
0
0
0
0
0
0
···
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
0
0
0
0
0
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
0
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"`
0
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 {