+6
-6
README.md
+6
-6
README.md
···
59
59
return err
60
60
}
61
61
62
-
k, err := oauth.ParseJWKFromBytes(b)
62
+
k, err := helpers.ParseJWKFromBytes(b)
63
63
if err != nil {
64
64
return err
65
65
}
···
69
69
return err
70
70
}
71
71
72
-
return e.JSON(200, oauth.CreateJwksResponseObject(pubKey))
72
+
return e.JSON(200, helpers.CreateJwksResponseObject(pubKey))
73
73
}
74
74
```
75
75
···
90
90
return err
91
91
}
92
92
93
-
k, err := oauth.ParseJWKFromBytes(b)
93
+
k, err := helpers.ParseJWKFromBytes(b)
94
94
if err != nil {
95
95
return err
96
96
}
97
97
98
-
cli, err := oauth.NewClient(oauth.ClientArgs{
98
+
cli, err := helpers.NewClient(oauth.ClientArgs{
99
99
ClientJwk: k,
100
100
ClientId: clientId,
101
101
RedirectUri: callbackUrl,
···
147
147
You'll need to create a private DPoP JWK for the user before directing them to their PDS to authenticate. You'll need to store this in a later step, and you will need to pass it along inside the PAR request, so go ahead and marshal it as well.
148
148
149
149
```go
150
-
k, err := oauth.GenerateKey(nil)
150
+
k, err := helpers.GenerateKey(nil)
151
151
if err != nil {
152
152
return err
153
153
}
···
268
268
oauthSession, err := s.getOauthSession(e.Request().Context(), did)
269
269
270
270
// Parse the user's JWK to pass into arguments
271
-
privateJwk, err := oauth.ParseJWKFromBytes([]byte(oauthSession.DpopPrivateJwk))
271
+
privateJwk, err := helpers.ParseJWKFromBytes([]byte(oauthSession.DpopPrivateJwk))
272
272
if err != nil {
273
273
return nil, false, err
274
274
}
+2
-2
cmd/helper/main.go
+2
-2
cmd/helper/main.go
···
4
4
"encoding/json"
5
5
"os"
6
6
7
-
oauth "github.com/haileyok/atproto-oauth-golang"
7
+
oauth_helpers "github.com/haileyok/atproto-oauth-golang/helpers"
8
8
"github.com/urfave/cli/v2"
9
9
)
10
10
···
33
33
inputPrefix := cmd.String("prefix")
34
34
prefix = &inputPrefix
35
35
}
36
-
key, err := oauth.GenerateKey(prefix)
36
+
key, err := oauth_helpers.GenerateKey(prefix)
37
37
if err != nil {
38
38
return err
39
39
}
cmd/web_server_demo/client_test
cmd/web_server_demo/client_test
This is a binary file and will not be displayed.
+3
-3
cmd/web_server_demo/handle_auth.go
+3
-3
cmd/web_server_demo/handle_auth.go
···
9
9
10
10
"github.com/bluesky-social/indigo/atproto/syntax"
11
11
"github.com/gorilla/sessions"
12
-
oauth "github.com/haileyok/atproto-oauth-golang"
12
+
oauth_helpers "github.com/haileyok/atproto-oauth-golang/helpers"
13
13
"github.com/labstack/echo-contrib/session"
14
14
"github.com/labstack/echo/v4"
15
15
"gorm.io/gorm/clause"
···
71
71
return err
72
72
}
73
73
74
-
dpopPrivateKey, err := oauth.GenerateKey(nil)
74
+
dpopPrivateKey, err := oauth_helpers.GenerateKey(nil)
75
75
if err != nil {
76
76
return err
77
77
}
···
159
159
return fmt.Errorf("incoming iss did not match authserver iss")
160
160
}
161
161
162
-
jwk, err := oauth.ParseJWKFromBytes([]byte(oauthRequest.DpopPrivateJwk))
162
+
jwk, err := oauth_helpers.ParseJWKFromBytes([]byte(oauthRequest.DpopPrivateJwk))
163
163
if err != nil {
164
164
return err
165
165
}
+4
-3
cmd/web_server_demo/main.go
+4
-3
cmd/web_server_demo/main.go
···
11
11
12
12
"github.com/gorilla/sessions"
13
13
oauth "github.com/haileyok/atproto-oauth-golang"
14
+
oauth_helpers "github.com/haileyok/atproto-oauth-golang/helpers"
14
15
_ "github.com/joho/godotenv/autoload"
15
16
"github.com/labstack/echo-contrib/session"
16
17
"github.com/labstack/echo/v4"
···
51
52
db *gorm.DB
52
53
oauthClient *oauth.Client
53
54
xrpcCli *oauth.XrpcClient
54
-
jwksResponse *oauth.JwksResponseObject
55
+
jwksResponse *oauth_helpers.JwksResponseObject
55
56
}
56
57
57
58
type TemplateRenderer struct {
···
100
101
return nil, err
101
102
}
102
103
103
-
k, err := oauth.ParseJWKFromBytes(b)
104
+
k, err := oauth_helpers.ParseJWKFromBytes(b)
104
105
if err != nil {
105
106
return nil, err
106
107
}
···
145
146
db: db,
146
147
oauthClient: c,
147
148
xrpcCli: xrpcCli,
148
-
jwksResponse: oauth.CreateJwksResponseObject(pubKey),
149
+
jwksResponse: oauth_helpers.CreateJwksResponseObject(pubKey),
149
150
}, nil
150
151
}
151
152
+3
-2
cmd/web_server_demo/user.go
+3
-2
cmd/web_server_demo/user.go
···
6
6
"time"
7
7
8
8
oauth "github.com/haileyok/atproto-oauth-golang"
9
+
oauth_helpers "github.com/haileyok/atproto-oauth-golang/helpers"
9
10
"github.com/labstack/echo-contrib/session"
10
11
"github.com/labstack/echo/v4"
11
12
)
···
21
22
}
22
23
23
24
if oauthSession.Expiration.Sub(time.Now()) <= 5*time.Minute {
24
-
privateJwk, err := oauth.ParseJWKFromBytes([]byte(oauthSession.DpopPrivateJwk))
25
+
privateJwk, err := oauth_helpers.ParseJWKFromBytes([]byte(oauthSession.DpopPrivateJwk))
25
26
if err != nil {
26
27
return nil, err
27
28
}
···
59
60
60
61
oauthSession, err := s.getOauthSession(e.Request().Context(), did)
61
62
62
-
privateJwk, err := oauth.ParseJWKFromBytes([]byte(oauthSession.DpopPrivateJwk))
63
+
privateJwk, err := oauth_helpers.ParseJWKFromBytes([]byte(oauthSession.DpopPrivateJwk))
63
64
if err != nil {
64
65
return nil, false, err
65
66
}
+4
-23
generic.go
helpers/generic.go
+4
-23
generic.go
helpers/generic.go
···
1
-
package oauth
1
+
package helpers
2
2
3
3
import (
4
4
"crypto/ecdsa"
5
5
"crypto/elliptic"
6
6
"crypto/rand"
7
-
"crypto/sha256"
8
-
"encoding/base64"
9
-
"encoding/hex"
10
7
"fmt"
11
8
"net/url"
12
9
"time"
···
39
36
return key, nil
40
37
}
41
38
42
-
func isSafeAndParsed(ustr string) (*url.URL, error) {
39
+
func IsUrlSafeAndParsed(ustr string) (*url.URL, error) {
43
40
u, err := url.Parse(ustr)
44
41
if err != nil {
45
42
return nil, err
···
64
61
return u, nil
65
62
}
66
63
67
-
func getPrivateKey(key jwk.Key) (*ecdsa.PrivateKey, error) {
64
+
func GetPrivateKey(key jwk.Key) (*ecdsa.PrivateKey, error) {
68
65
var pkey ecdsa.PrivateKey
69
66
if err := key.Raw(&pkey); err != nil {
70
67
return nil, err
···
73
70
return &pkey, nil
74
71
}
75
72
76
-
func getPublicKey(key jwk.Key) (*ecdsa.PublicKey, error) {
73
+
func GetPublicKey(key jwk.Key) (*ecdsa.PublicKey, error) {
77
74
var pkey ecdsa.PublicKey
78
75
if err := key.Raw(&pkey); err != nil {
79
76
return nil, err
···
95
92
func ParseJWKFromBytes(b []byte) (jwk.Key, error) {
96
93
return jwk.ParseKey(b)
97
94
}
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
-
}
+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
13
14
14
"github.com/golang-jwt/jwt/v5"
15
15
"github.com/google/uuid"
16
+
"github.com/haileyok/atproto-oauth-golang/helpers"
17
+
internal_helpers "github.com/haileyok/atproto-oauth-golang/internal/helpers"
16
18
"github.com/lestrrat-go/jwx/v2/jwk"
17
19
)
18
20
···
46
48
}
47
49
}
48
50
49
-
clientPkey, err := getPrivateKey(args.ClientJwk)
51
+
clientPkey, err := helpers.GetPrivateKey(args.ClientJwk)
50
52
if err != nil {
51
53
return nil, fmt.Errorf("could not load private key from provided client jwk: %w", err)
52
54
}
···
63
65
}
64
66
65
67
func (c *Client) ResolvePdsAuthServer(ctx context.Context, ustr string) (string, error) {
66
-
u, err := isSafeAndParsed(ustr)
68
+
u, err := helpers.IsUrlSafeAndParsed(ustr)
67
69
if err != nil {
68
70
return "", err
69
71
}
···
104
106
}
105
107
106
108
func (c *Client) FetchAuthServerMetadata(ctx context.Context, ustr string) (*OauthAuthorizationMetadata, error) {
107
-
u, err := isSafeAndParsed(ustr)
109
+
u, err := helpers.IsUrlSafeAndParsed(ustr)
108
110
if err != nil {
109
111
return nil, err
110
112
}
···
219
221
220
222
parUrl := authServerMeta.PushedAuthorizationRequestEndpoint
221
223
222
-
state, err := generateToken(10)
224
+
state, err := internal_helpers.GenerateToken(10)
223
225
if err != nil {
224
226
return nil, fmt.Errorf("could not generate state token: %w", err)
225
227
}
226
228
227
-
pkceVerifier, err := generateToken(48)
229
+
pkceVerifier, err := internal_helpers.GenerateToken(48)
228
230
if err != nil {
229
231
return nil, fmt.Errorf("could not generate pkce verifier: %w", err)
230
232
}
231
233
232
-
codeChallenge := generateCodeChallenge(pkceVerifier)
234
+
codeChallenge := internal_helpers.GenerateCodeChallenge(pkceVerifier)
233
235
codeChallengeMethod := "S256"
234
236
235
237
clientAssertion, err := c.ClientAssertionJwt(authServerUrl)
···
259
261
params.Set("login_hint", loginHint)
260
262
}
261
263
262
-
_, err = isSafeAndParsed(parUrl)
264
+
_, err = helpers.IsUrlSafeAndParsed(parUrl)
263
265
if err != nil {
264
266
return nil, err
265
267
}
+4
-3
oauth_test.go
+4
-3
oauth_test.go
···
8
8
"os"
9
9
"testing"
10
10
11
+
"github.com/haileyok/atproto-oauth-golang/helpers"
11
12
_ "github.com/joho/godotenv/autoload"
12
13
"github.com/stretchr/testify/assert"
13
14
)
···
27
28
panic(err)
28
29
}
29
30
30
-
k, err := ParseJWKFromBytes(b)
31
+
k, err := helpers.ParseJWKFromBytes(b)
31
32
if err != nil {
32
33
panic(err)
33
34
}
···
81
82
assert := assert.New(t)
82
83
83
84
prefix := "testing"
84
-
_, err := GenerateKey(&prefix)
85
+
_, err := helpers.GenerateKey(&prefix)
85
86
assert.NoError(err)
86
87
}
87
88
···
95
96
}
96
97
97
98
prefix := "testing"
98
-
dpopPriv, err := GenerateKey(&prefix)
99
+
dpopPriv, err := helpers.GenerateKey(&prefix)
99
100
if err != nil {
100
101
panic(err)
101
102
}
+2
-1
xrpc.go
+2
-1
xrpc.go
···
16
16
"github.com/carlmjohnson/versioninfo"
17
17
"github.com/golang-jwt/jwt/v5"
18
18
"github.com/google/uuid"
19
+
"github.com/haileyok/atproto-oauth-golang/internal/helpers"
19
20
"github.com/lestrrat-go/jwx/v2/jwk"
20
21
)
21
22
···
115
116
"jti": uuid.NewString(),
116
117
"htm": method,
117
118
"htu": url,
118
-
"ath": generateCodeChallenge(accessToken),
119
+
"ath": helpers.GenerateCodeChallenge(accessToken),
119
120
}
120
121
121
122
if nonce != "" {