Skip to content

Commit

Permalink
Merge pull request #2737 from howjmay/auth-refactor
Browse files Browse the repository at this point in the history
refactor: Refactor authentication lib
  • Loading branch information
jorgemmsilva authored Aug 7, 2023
2 parents 672f92d + 32a0093 commit c614bdf
Show file tree
Hide file tree
Showing 22 changed files with 407 additions and 603 deletions.
19 changes: 19 additions & 0 deletions packages/authentication/auth_context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package authentication

import "github.com/labstack/echo/v4"

type AuthContext struct {
echo.Context

scheme string
claims *WaspClaims
name string
}

func (a *AuthContext) Name() string {
return a.name
}

func (a *AuthContext) Scheme() string {
return a.scheme
}
35 changes: 0 additions & 35 deletions packages/authentication/basic_auth.go

This file was deleted.

44 changes: 0 additions & 44 deletions packages/authentication/context.go

This file was deleted.

53 changes: 0 additions & 53 deletions packages/authentication/ip_whitelist.go

This file was deleted.

146 changes: 26 additions & 120 deletions packages/authentication/jwt_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,12 @@ import (
"crypto/subtle"
"fmt"
"net/http"
"strings"
"time"

"github.com/golang-jwt/jwt/v5"
echojwt "github.com/labstack/echo-jwt/v4"
"github.com/labstack/echo/v4"

"github.com/iotaledger/wasp/packages/authentication/shared"
"github.com/iotaledger/wasp/packages/authentication/shared/permissions"
"github.com/iotaledger/wasp/packages/users"
)

// Errors
Expand All @@ -32,8 +28,6 @@ type JWTAuth struct {
secret []byte
}

type MiddlewareValidator = func(c echo.Context, authContext *AuthContext) bool

func NewJWTAuth(duration time.Duration, nodeID string, secret []byte) *JWTAuth {
return &JWTAuth{
duration: duration,
Expand All @@ -42,6 +36,32 @@ func NewJWTAuth(duration time.Duration, nodeID string, secret []byte) *JWTAuth {
}
}

func (j *JWTAuth) IssueJWT(username string, claims *WaspClaims) (string, error) {
now := time.Now()

// Set claims
registeredClaims := jwt.RegisteredClaims{
Subject: username,
Issuer: j.nodeID,
Audience: jwt.ClaimStrings{j.nodeID},
ID: fmt.Sprintf("%d", now.Unix()),
IssuedAt: jwt.NewNumericDate(now),
NotBefore: jwt.NewNumericDate(now),
}

if j.duration > 0 {
registeredClaims.ExpiresAt = jwt.NewNumericDate(now.Add(j.duration))
}

claims.RegisteredClaims = registeredClaims

// Create token
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

// Generate encoded token and send it as response.
return token.SignedString(j.secret)
}

type WaspClaims struct {
jwt.RegisteredClaims
Permissions map[string]struct{} `json:"permissions"`
Expand Down Expand Up @@ -78,117 +98,3 @@ func (c *WaspClaims) compare(field, expected string) bool {
func (c *WaspClaims) VerifySubject(expected string) bool {
return c.compare(c.Subject, expected)
}

func (j *JWTAuth) IssueJWT(username string, authClaims *WaspClaims) (string, error) {
now := time.Now()

// Set claims
registeredClaims := jwt.RegisteredClaims{
Subject: username,
Issuer: j.nodeID,
Audience: jwt.ClaimStrings{j.nodeID},
ID: fmt.Sprintf("%d", now.Unix()),
IssuedAt: jwt.NewNumericDate(now),
NotBefore: jwt.NewNumericDate(now),
}

if j.duration > 0 {
registeredClaims.ExpiresAt = jwt.NewNumericDate(now.Add(j.duration))
}

authClaims.RegisteredClaims = registeredClaims

// Create token
token := jwt.NewWithClaims(jwt.SigningMethodHS256, authClaims)

// Generate encoded token and send it as response.
return token.SignedString(j.secret)
}

var DefaultJWTDuration time.Duration

func AddJWTAuth(config JWTAuthConfiguration, privateKey []byte, userManager *users.UserManager, claimValidator ClaimValidator) (*JWTAuth, func() echo.MiddlewareFunc) {
duration := config.Duration

// If durationHours is 0, we set 24h as the default duration
if duration == 0 {
duration = DefaultJWTDuration
}

// FIXME: replace "wasp" as nodeID
jwtAuth := NewJWTAuth(duration, "wasp", privateKey)

authMiddleware := func() echo.MiddlewareFunc {
return echojwt.WithConfig(echojwt.Config{
ContextKey: JWTContextKey,
NewClaimsFunc: func(c echo.Context) jwt.Claims {
return &WaspClaims{}
},
Skipper: func(c echo.Context) bool {
path := c.Request().URL.Path
if path == "/" ||
strings.HasSuffix(path, shared.AuthRoute()) ||
strings.HasSuffix(path, shared.AuthInfoRoute()) ||
strings.HasPrefix(path, "/doc") {
return true
}

return false
},
SigningKey: jwtAuth.secret,
TokenLookup: "header:Authorization:Bearer ,cookie:jwt",
ParseTokenFunc: func(c echo.Context, auth string) (interface{}, error) {
keyFunc := func(t *jwt.Token) (interface{}, error) {
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"])
}

return jwtAuth.secret, nil
}

token, err := jwt.ParseWithClaims(
auth,
&WaspClaims{},
keyFunc,
jwt.WithValidMethods([]string{"HS256"}),
)
if err != nil {
return nil, err
}
if !token.Valid {
return nil, fmt.Errorf("invalid token")
}

claims, ok := token.Claims.(*WaspClaims)
if !ok {
return nil, fmt.Errorf("wrong JWT claim type")
}

audience, err := claims.GetAudience()
if err != nil {
return nil, err
}
b, err := audience.MarshalJSON()
if err != nil {
return nil, err
}
if subtle.ConstantTimeCompare(b, []byte(fmt.Sprintf("[%q]", jwtAuth.nodeID))) == 0 {
return nil, fmt.Errorf("not in audience")
}

userMap := userManager.Users()
if _, ok := userMap[claims.Subject]; !ok {
return nil, fmt.Errorf("invalid subject")
}

authContext := c.Get("auth").(*AuthContext)
authContext.isAuthenticated = true
authContext.claims = claims

return token, nil
},
})
}

return jwtAuth, authMiddleware
}
Loading

0 comments on commit c614bdf

Please sign in to comment.