+2
-2
cmd/helper/main.go
+2
-2
cmd/helper/main.go
cmd/web_server_demo/client_test
cmd/web_server_demo/client_test
This is a binary file and will not be displayed.
+4
-3
cmd/web_server_demo/main.go
+4
-3
cmd/web_server_demo/main.go
···
11
12
"github.com/gorilla/sessions"
13
oauth "github.com/haileyok/atproto-oauth-golang"
14
_ "github.com/joho/godotenv/autoload"
15
"github.com/labstack/echo-contrib/session"
16
"github.com/labstack/echo/v4"
···
51
db *gorm.DB
52
oauthClient *oauth.Client
53
xrpcCli *oauth.XrpcClient
54
-
jwksResponse *oauth.JwksResponseObject
55
}
56
57
type TemplateRenderer struct {
···
100
return nil, err
101
}
102
103
-
k, err := oauth.ParseJWKFromBytes(b)
104
if err != nil {
105
return nil, err
106
}
···
145
db: db,
146
oauthClient: c,
147
xrpcCli: xrpcCli,
148
-
jwksResponse: oauth.CreateJwksResponseObject(pubKey),
149
}, nil
150
}
151
···
11
12
"github.com/gorilla/sessions"
13
oauth "github.com/haileyok/atproto-oauth-golang"
14
+
oauth_helpers "github.com/haileyok/atproto-oauth-golang/helpers"
15
_ "github.com/joho/godotenv/autoload"
16
"github.com/labstack/echo-contrib/session"
17
"github.com/labstack/echo/v4"
···
52
db *gorm.DB
53
oauthClient *oauth.Client
54
xrpcCli *oauth.XrpcClient
55
+
jwksResponse *oauth_helpers.JwksResponseObject
56
}
57
58
type TemplateRenderer struct {
···
101
return nil, err
102
}
103
104
+
k, err := oauth_helpers.ParseJWKFromBytes(b)
105
if err != nil {
106
return nil, err
107
}
···
146
db: db,
147
oauthClient: c,
148
xrpcCli: xrpcCli,
149
+
jwksResponse: oauth_helpers.CreateJwksResponseObject(pubKey),
150
}, nil
151
}
152
+3
-2
cmd/web_server_demo/user.go
+3
-2
cmd/web_server_demo/user.go
···
6
"time"
7
8
oauth "github.com/haileyok/atproto-oauth-golang"
9
"github.com/labstack/echo-contrib/session"
10
"github.com/labstack/echo/v4"
11
)
···
21
}
22
23
if oauthSession.Expiration.Sub(time.Now()) <= 5*time.Minute {
24
-
privateJwk, err := oauth.ParseJWKFromBytes([]byte(oauthSession.DpopPrivateJwk))
25
if err != nil {
26
return nil, err
27
}
···
59
60
oauthSession, err := s.getOauthSession(e.Request().Context(), did)
61
62
-
privateJwk, err := oauth.ParseJWKFromBytes([]byte(oauthSession.DpopPrivateJwk))
63
if err != nil {
64
return nil, false, err
65
}
···
6
"time"
7
8
oauth "github.com/haileyok/atproto-oauth-golang"
9
+
oauth_helpers "github.com/haileyok/atproto-oauth-golang/helpers"
10
"github.com/labstack/echo-contrib/session"
11
"github.com/labstack/echo/v4"
12
)
···
22
}
23
24
if oauthSession.Expiration.Sub(time.Now()) <= 5*time.Minute {
25
+
privateJwk, err := oauth_helpers.ParseJWKFromBytes([]byte(oauthSession.DpopPrivateJwk))
26
if err != nil {
27
return nil, err
28
}
···
60
61
oauthSession, err := s.getOauthSession(e.Request().Context(), did)
62
63
+
privateJwk, err := oauth_helpers.ParseJWKFromBytes([]byte(oauthSession.DpopPrivateJwk))
64
if err != nil {
65
return nil, false, err
66
}
+4
-23
generic.go
helpers/generic.go
+4
-23
generic.go
helpers/generic.go
···
1
-
package oauth
2
3
import (
4
"crypto/ecdsa"
5
"crypto/elliptic"
6
"crypto/rand"
7
-
"crypto/sha256"
8
-
"encoding/base64"
9
-
"encoding/hex"
10
"fmt"
11
"net/url"
12
"time"
···
39
return key, nil
40
}
41
42
-
func isSafeAndParsed(ustr string) (*url.URL, error) {
43
u, err := url.Parse(ustr)
44
if err != nil {
45
return nil, err
···
64
return u, nil
65
}
66
67
-
func getPrivateKey(key jwk.Key) (*ecdsa.PrivateKey, error) {
68
var pkey ecdsa.PrivateKey
69
if err := key.Raw(&pkey); err != nil {
70
return nil, err
···
73
return &pkey, nil
74
}
75
76
-
func getPublicKey(key jwk.Key) (*ecdsa.PublicKey, error) {
77
var pkey ecdsa.PublicKey
78
if err := key.Raw(&pkey); err != nil {
79
return nil, err
···
95
func ParseJWKFromBytes(b []byte) (jwk.Key, error) {
96
return jwk.ParseKey(b)
97
}
98
-
99
-
func generateToken(len int) (string, error) {
100
-
b := make([]byte, len)
101
-
if _, err := rand.Read(b); err != nil {
102
-
return "", err
103
-
}
104
-
105
-
return hex.EncodeToString(b), nil
106
-
}
107
-
108
-
func generateCodeChallenge(pkceVerifier string) string {
109
-
h := sha256.New()
110
-
h.Write([]byte(pkceVerifier))
111
-
hash := h.Sum(nil)
112
-
return base64.RawURLEncoding.EncodeToString(hash)
113
-
}
···
1
+
package helpers
2
3
import (
4
"crypto/ecdsa"
5
"crypto/elliptic"
6
"crypto/rand"
7
"fmt"
8
"net/url"
9
"time"
···
36
return key, nil
37
}
38
39
+
func IsUrlSafeAndParsed(ustr string) (*url.URL, error) {
40
u, err := url.Parse(ustr)
41
if err != nil {
42
return nil, err
···
61
return u, nil
62
}
63
64
+
func GetPrivateKey(key jwk.Key) (*ecdsa.PrivateKey, error) {
65
var pkey ecdsa.PrivateKey
66
if err := key.Raw(&pkey); err != nil {
67
return nil, err
···
70
return &pkey, nil
71
}
72
73
+
func GetPublicKey(key jwk.Key) (*ecdsa.PublicKey, error) {
74
var pkey ecdsa.PublicKey
75
if err := key.Raw(&pkey); err != nil {
76
return nil, err
···
92
func ParseJWKFromBytes(b []byte) (jwk.Key, error) {
93
return jwk.ParseKey(b)
94
}
+24
internal/helpers/generic.go
+24
internal/helpers/generic.go
···
···
1
+
package helpers
2
+
3
+
import (
4
+
"crypto/rand"
5
+
"crypto/sha256"
6
+
"encoding/base64"
7
+
"encoding/hex"
8
+
)
9
+
10
+
func GenerateToken(len int) (string, error) {
11
+
b := make([]byte, len)
12
+
if _, err := rand.Read(b); err != nil {
13
+
return "", err
14
+
}
15
+
16
+
return hex.EncodeToString(b), nil
17
+
}
18
+
19
+
func GenerateCodeChallenge(pkceVerifier string) string {
20
+
h := sha256.New()
21
+
h.Write([]byte(pkceVerifier))
22
+
hash := h.Sum(nil)
23
+
return base64.RawURLEncoding.EncodeToString(hash)
24
+
}
+9
-7
oauth.go
+9
-7
oauth.go
···
13
14
"github.com/golang-jwt/jwt/v5"
15
"github.com/google/uuid"
16
"github.com/lestrrat-go/jwx/v2/jwk"
17
)
18
···
46
}
47
}
48
49
-
clientPkey, err := getPrivateKey(args.ClientJwk)
50
if err != nil {
51
return nil, fmt.Errorf("could not load private key from provided client jwk: %w", err)
52
}
···
63
}
64
65
func (c *Client) ResolvePdsAuthServer(ctx context.Context, ustr string) (string, error) {
66
-
u, err := isSafeAndParsed(ustr)
67
if err != nil {
68
return "", err
69
}
···
104
}
105
106
func (c *Client) FetchAuthServerMetadata(ctx context.Context, ustr string) (*OauthAuthorizationMetadata, error) {
107
-
u, err := isSafeAndParsed(ustr)
108
if err != nil {
109
return nil, err
110
}
···
219
220
parUrl := authServerMeta.PushedAuthorizationRequestEndpoint
221
222
-
state, err := generateToken(10)
223
if err != nil {
224
return nil, fmt.Errorf("could not generate state token: %w", err)
225
}
226
227
-
pkceVerifier, err := generateToken(48)
228
if err != nil {
229
return nil, fmt.Errorf("could not generate pkce verifier: %w", err)
230
}
231
232
-
codeChallenge := generateCodeChallenge(pkceVerifier)
233
codeChallengeMethod := "S256"
234
235
clientAssertion, err := c.ClientAssertionJwt(authServerUrl)
···
259
params.Set("login_hint", loginHint)
260
}
261
262
-
_, err = isSafeAndParsed(parUrl)
263
if err != nil {
264
return nil, err
265
}
···
13
14
"github.com/golang-jwt/jwt/v5"
15
"github.com/google/uuid"
16
+
"github.com/haileyok/atproto-oauth-golang/helpers"
17
+
internal_helpers "github.com/haileyok/atproto-oauth-golang/internal/helpers"
18
"github.com/lestrrat-go/jwx/v2/jwk"
19
)
20
···
48
}
49
}
50
51
+
clientPkey, err := helpers.GetPrivateKey(args.ClientJwk)
52
if err != nil {
53
return nil, fmt.Errorf("could not load private key from provided client jwk: %w", err)
54
}
···
65
}
66
67
func (c *Client) ResolvePdsAuthServer(ctx context.Context, ustr string) (string, error) {
68
+
u, err := helpers.IsUrlSafeAndParsed(ustr)
69
if err != nil {
70
return "", err
71
}
···
106
}
107
108
func (c *Client) FetchAuthServerMetadata(ctx context.Context, ustr string) (*OauthAuthorizationMetadata, error) {
109
+
u, err := helpers.IsUrlSafeAndParsed(ustr)
110
if err != nil {
111
return nil, err
112
}
···
221
222
parUrl := authServerMeta.PushedAuthorizationRequestEndpoint
223
224
+
state, err := internal_helpers.GenerateToken(10)
225
if err != nil {
226
return nil, fmt.Errorf("could not generate state token: %w", err)
227
}
228
229
+
pkceVerifier, err := internal_helpers.GenerateToken(48)
230
if err != nil {
231
return nil, fmt.Errorf("could not generate pkce verifier: %w", err)
232
}
233
234
+
codeChallenge := internal_helpers.GenerateCodeChallenge(pkceVerifier)
235
codeChallengeMethod := "S256"
236
237
clientAssertion, err := c.ClientAssertionJwt(authServerUrl)
···
261
params.Set("login_hint", loginHint)
262
}
263
264
+
_, err = helpers.IsUrlSafeAndParsed(parUrl)
265
if err != nil {
266
return nil, err
267
}
+4
-3
oauth_test.go
+4
-3
oauth_test.go
···
8
"os"
9
"testing"
10
11
_ "github.com/joho/godotenv/autoload"
12
"github.com/stretchr/testify/assert"
13
)
···
27
panic(err)
28
}
29
30
-
k, err := ParseJWKFromBytes(b)
31
if err != nil {
32
panic(err)
33
}
···
81
assert := assert.New(t)
82
83
prefix := "testing"
84
-
_, err := GenerateKey(&prefix)
85
assert.NoError(err)
86
}
87
···
95
}
96
97
prefix := "testing"
98
-
dpopPriv, err := GenerateKey(&prefix)
99
if err != nil {
100
panic(err)
101
}
···
8
"os"
9
"testing"
10
11
+
"github.com/haileyok/atproto-oauth-golang/helpers"
12
_ "github.com/joho/godotenv/autoload"
13
"github.com/stretchr/testify/assert"
14
)
···
28
panic(err)
29
}
30
31
+
k, err := helpers.ParseJWKFromBytes(b)
32
if err != nil {
33
panic(err)
34
}
···
82
assert := assert.New(t)
83
84
prefix := "testing"
85
+
_, err := helpers.GenerateKey(&prefix)
86
assert.NoError(err)
87
}
88
···
96
}
97
98
prefix := "testing"
99
+
dpopPriv, err := helpers.GenerateKey(&prefix)
100
if err != nil {
101
panic(err)
102
}
+2
-1
xrpc.go
+2
-1
xrpc.go
···
16
"github.com/carlmjohnson/versioninfo"
17
"github.com/golang-jwt/jwt/v5"
18
"github.com/google/uuid"
19
"github.com/lestrrat-go/jwx/v2/jwk"
20
)
21
···
115
"jti": uuid.NewString(),
116
"htm": method,
117
"htu": url,
118
-
"ath": generateCodeChallenge(accessToken),
119
}
120
121
if nonce != "" {
···
16
"github.com/carlmjohnson/versioninfo"
17
"github.com/golang-jwt/jwt/v5"
18
"github.com/google/uuid"
19
+
"github.com/haileyok/atproto-oauth-golang/internal/helpers"
20
"github.com/lestrrat-go/jwx/v2/jwk"
21
)
22
···
116
"jti": uuid.NewString(),
117
"htm": method,
118
"htu": url,
119
+
"ath": helpers.GenerateCodeChallenge(accessToken),
120
}
121
122
if nonce != "" {