Skip to content

Commit

Permalink
Update dependencies and PostgreSQL image (#10)
Browse files Browse the repository at this point in the history
* Remove vendor

* Update all dependencies

* Remove CustomKeyMeta from magiclink package

* Update jwks.go

* Update tests

* Upgrade more magiclink files

* Fix bug caught by tests

* Upgrade a bit

* Full reimplementation for upgrade

* Upgrade test storage

* Update setup

* Fix refactor bug

* Upgrade handlers

* Upgrade some tests

* More upgrades

* Fix some tests

* Tests pass again

* Remove custom key meta

* Remove generic custom create args

* Add TODO

* Edit TODO

* Remove custom read response

* Add TODO

* Remove unused

* Move claims to top level package

* Remove nested package

* Add semicolon to terminate statement

* Change startup.sql path

* Use correct path

* Add default signing key test back

* Remove unused test

* Respect visited and expires for in memory implementation

* Update PostgreSQL version

* Update deps more

* IDE keeps updating toolchain
  • Loading branch information
MicahParks authored Oct 4, 2024
1 parent 852776a commit 9f0ecac
Show file tree
Hide file tree
Showing 643 changed files with 725 additions and 275,666 deletions.
54 changes: 54 additions & 0 deletions claims.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package magiclinksdev

import (
"encoding/json"
"fmt"
"time"

"github.com/golang-jwt/jwt/v5"
"github.com/tidwall/gjson"
)

const (
AttrIss = "iss"
AttrSub = "sub"
AttrAud = "aud"
AttrExp = "exp"
AttrNbf = "nbf"
AttrIat = "iat"
AttrJti = "jti"
)

// SigningBytesClaims is a JWT claims type that allows for signing claims represented in bytes.
type SigningBytesClaims struct {
Claims json.RawMessage
}

func (s SigningBytesClaims) GetExpirationTime() (*jwt.NumericDate, error) {
return jwt.NewNumericDate(time.Unix(gjson.GetBytes(s.Claims, AttrExp).Int(), 0)), nil
}
func (s SigningBytesClaims) GetIssuedAt() (*jwt.NumericDate, error) {
return jwt.NewNumericDate(time.Unix(gjson.GetBytes(s.Claims, AttrIat).Int(), 0)), nil
}
func (s SigningBytesClaims) GetNotBefore() (*jwt.NumericDate, error) {
return jwt.NewNumericDate(time.Unix(gjson.GetBytes(s.Claims, AttrNbf).Int(), 0)), nil
}
func (s SigningBytesClaims) GetIssuer() (string, error) {
return gjson.GetBytes(s.Claims, AttrIss).String(), nil
}
func (s SigningBytesClaims) GetSubject() (string, error) {
return gjson.GetBytes(s.Claims, AttrSub).String(), nil
}
func (s SigningBytesClaims) GetAudience() (jwt.ClaimStrings, error) {
var aud jwt.ClaimStrings
err := json.Unmarshal([]byte(gjson.GetBytes(s.Claims, AttrAud).Raw), &aud)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal audience: %w", err)
}
return aud, nil
}

// MarshalJSON helps implement the json.Marshaler interface.
func (s SigningBytesClaims) MarshalJSON() ([]byte, error) {
return s.Claims, nil
}
32 changes: 10 additions & 22 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import (
"fmt"
"net/http"
"net/url"
"time"
"slices"

"github.com/MicahParks/keyfunc"
"github.com/golang-jwt/jwt/v4"
"github.com/MicahParks/keyfunc/v3"
"github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"

mld "github.com/MicahParks/magiclinksdev"
Expand Down Expand Up @@ -50,7 +50,7 @@ type Client struct {
baseURL *url.URL
http *http.Client
jwtIss string
jwks *keyfunc.JWKS
keyf keyfunc.Keyfunc
}

// New creates a new magiclinksdev client. The apiKey and aud are tied to the service account being used. The baseURL is
Expand Down Expand Up @@ -86,17 +86,7 @@ func New(apiKey, aud uuid.UUID, baseURL, iss string, options Options) (Client, e
if err != nil {
return Client{}, fmt.Errorf("failed to parse JWKS URL: %w", err)
}
opt := keyfunc.Options{
Client: c.http,
RefreshInterval: time.Hour,
RefreshRateLimit: 5 * time.Minute,
RefreshTimeout: time.Minute,
RefreshUnknownKID: true,
}
if options.KeyfuncOptions != nil {
opt = *options.KeyfuncOptions
}
c.jwks, err = keyfunc.Get(jwksURL.String(), opt)
c.keyf, err = keyfunc.NewDefault([]string{jwksURL.String()})
if err != nil {
return Client{}, fmt.Errorf("failed to get JWKS: %w", err)
}
Expand All @@ -110,12 +100,12 @@ func New(apiKey, aud uuid.UUID, baseURL, iss string, options Options) (Client, e
// the documentation for jwt.ParseWithClaims for more information. Registered JWT claims will be validated regardless if
// claims are specified or not.
func (c Client) LocalJWTValidate(token string, claims jwt.Claims) (*jwt.Token, error) {
if c.jwks == nil {
if c.keyf == nil {
return nil, fmt.Errorf("%w: client configuration disabled JWK Set client, keyfunc, please enable keyfunc in magiclinksdev client creation options", ErrClientConfig)
}

var registered jwt.RegisteredClaims
t, err := jwt.ParseWithClaims(token, &registered, c.jwks.Keyfunc)
t, err := jwt.ParseWithClaims(token, &registered, c.keyf.Keyfunc)
if err != nil {
return nil, fmt.Errorf("failed to parse JWT: %w", err)
}
Expand All @@ -124,19 +114,17 @@ func (c Client) LocalJWTValidate(token string, claims jwt.Claims) (*jwt.Token, e
return nil, fmt.Errorf("%w: invalid JWT", handle.ErrToken)
}

validAud := registered.VerifyAudience(c.aud.String(), true)
if !validAud {
if !slices.Contains(registered.Audience, c.aud.String()) {
return nil, fmt.Errorf("%w: invalid JWT audience, this token is likely signed for another service account under the same mld instance", handle.ErrToken)
}
if c.jwtIss != "" {
validIss := registered.VerifyIssuer(c.jwtIss, true)
if !validIss {
if registered.Issuer != c.jwtIss {
return nil, fmt.Errorf("%w: invalid JWT issuer", handle.ErrToken)
}
}

if claims != nil {
t, err = jwt.ParseWithClaims(token, claims, c.jwks.Keyfunc)
t, err = jwt.ParseWithClaims(token, claims, c.keyf.Keyfunc)
if err != nil {
return nil, fmt.Errorf("failed to parse JWT: %w", err)
}
Expand Down
12 changes: 3 additions & 9 deletions client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import (

jt "github.com/MicahParks/jsontype"
"github.com/MicahParks/jwkset"
"github.com/MicahParks/keyfunc"
"github.com/golang-jwt/jwt/v4"
"github.com/MicahParks/keyfunc/v3"
"github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"

mld "github.com/MicahParks/magiclinksdev"
Expand Down Expand Up @@ -78,12 +78,6 @@ func TestNew(t *testing.T) {
disabledKeyfunc: true,
errIs: ErrClientConfig,
},
{
name: "Invalid JWK Set URL",
baseURL: "http://localhost:999999999",
keyfuncOpts: &keyfunc.Options{},
errAs: &url.Error{},
},
}

for _, c := range tc {
Expand Down Expand Up @@ -314,7 +308,7 @@ func jwtCreateHelper(ctx context.Context, t *testing.T, c Client) string {
}

claims := mldtest.TestClaims{}
token, err := jwt.ParseWithClaims(resp.JWTCreateResults.JWT, &claims, c.jwks.Keyfunc)
token, err := jwt.ParseWithClaims(resp.JWTCreateResults.JWT, &claims, c.keyf.Keyfunc)
if err != nil {
t.Fatalf("Failed to parse JWT: %v.", err)
}
Expand Down
10 changes: 5 additions & 5 deletions cmd/migrate/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (

mld "github.com/MicahParks/magiclinksdev"
"github.com/MicahParks/magiclinksdev/setup"
"github.com/MicahParks/magiclinksdev/storage/postgres"
"github.com/MicahParks/magiclinksdev/storage"
)

func main() {
Expand All @@ -23,26 +23,26 @@ func main() {

logger := setup.CreateLogger(conf.Server)

_, pool, err := postgres.New(ctx, conf.Storage)
_, pool, err := storage.New(ctx, conf.Storage)
if err != nil {
logger.ErrorContext(ctx, "Failed to create postgres pool.",
mld.LogErr, err,
)
os.Exit(1)
}

k, err := postgres.DecodeAES256Base64(conf.Storage.AES256KeyBase64)
k, err := storage.DecodeAES256Base64(conf.Storage.AES256KeyBase64)
if err != nil {
logger.ErrorContext(ctx, "Failed to decode AES256 key.",
mld.LogErr, err,
)
os.Exit(1)
}
options := postgres.MigratorOptions{
options := storage.MigratorOptions{
EncryptionKey: k,
Logger: logger,
}
migrator, err := postgres.NewMigrator(pool, options)
migrator, err := storage.NewMigrator(pool, options)
if err != nil {
logger.ErrorContext(ctx, "Failed to create migrator.",
mld.LogErr, err,
Expand Down
4 changes: 2 additions & 2 deletions docker-compose.yml → compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ services:
volumes:
- "./config.quickstart.json:/config.json"
mldpostgres:
image: "postgres:15"
image: "postgres:17"
environment:
POSTGRES_PASSWORD: "password"
ports:
- "5432:5432"
volumes:
- "./storage/postgres/startup.sql:/docker-entrypoint-initdb.d/startup.sql"
- "./storage/startup.sql:/docker-entrypoint-initdb.d/startup.sql"
4 changes: 4 additions & 0 deletions constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,7 @@ const (
// ResponseUnauthorized is the response for unauthorized requests.
ResponseUnauthorized = "Unauthorized."
)

func Ptr[T any](v T) *T {
return &v
}
4 changes: 2 additions & 2 deletions examples/server/create_keys/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
mld "github.com/MicahParks/magiclinksdev"
"github.com/MicahParks/magiclinksdev/network/middleware/ctxkey"
"github.com/MicahParks/magiclinksdev/setup"
"github.com/MicahParks/magiclinksdev/storage/postgres"
"github.com/MicahParks/magiclinksdev/storage"
)

func main() {
Expand All @@ -28,7 +28,7 @@ func main() {
os.Exit(1)
}

store, _, err := postgres.NewWithSetup(ctx, config.Storage, logger.With("postgresSetup", true))
store, _, err := storage.NewWithSetup(ctx, config.Storage, logger.With("postgresSetup", true))
if err != nil {
logger.ErrorContext(ctx, "Failed to create storage.",
mld.LogErr, err,
Expand Down
4 changes: 2 additions & 2 deletions examples/server/truncate_database/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
mld "github.com/MicahParks/magiclinksdev"
"github.com/MicahParks/magiclinksdev/network/middleware/ctxkey"
"github.com/MicahParks/magiclinksdev/setup"
"github.com/MicahParks/magiclinksdev/storage/postgres"
"github.com/MicahParks/magiclinksdev/storage"
)

func main() {
Expand All @@ -28,7 +28,7 @@ func main() {
os.Exit(1)
}

store, _, err := postgres.NewWithSetup(ctx, conf.Storage, logger.With("postgresSetup", true))
store, _, err := storage.NewWithSetup(ctx, conf.Storage, logger.With("postgresSetup", true))
if err != nil {
logger.ErrorContext(ctx, "Failed to create storage.",
mld.LogErr, err,
Expand Down
36 changes: 19 additions & 17 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,32 +1,34 @@
module github.com/MicahParks/magiclinksdev

go 1.21.1
go 1.22.0

toolchain go1.23.2

require (
github.com/MicahParks/jsontype v0.5.0
github.com/MicahParks/jwkset v0.3.1
github.com/MicahParks/keyfunc v1.9.0
github.com/MicahParks/jsontype v0.6.1
github.com/MicahParks/jwkset v0.5.20
github.com/MicahParks/keyfunc/v3 v3.3.5
github.com/MicahParks/recaptcha v0.0.5
github.com/aws/aws-sdk-go v1.45.6
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/google/uuid v1.3.1
github.com/jackc/pgx/v5 v5.4.3
github.com/sendgrid/sendgrid-go v3.13.0+incompatible
github.com/tidwall/gjson v1.16.0
github.com/aws/aws-sdk-go v1.55.5
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/google/uuid v1.6.0
github.com/jackc/pgx/v5 v5.7.1
github.com/sendgrid/sendgrid-go v3.16.0+incompatible
github.com/tidwall/gjson v1.18.0
github.com/tidwall/sjson v1.2.5
golang.org/x/mod v0.12.0
golang.org/x/time v0.3.0
golang.org/x/mod v0.21.0
golang.org/x/time v0.6.0
)

require (
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/sendgrid/rest v2.6.9+incompatible // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
golang.org/x/crypto v0.13.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/crypto v0.27.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/text v0.18.0 // indirect
)
Loading

0 comments on commit 9f0ecac

Please sign in to comment.