Skip to content

Commit

Permalink
feat(authN): Redesign JWT token auth (#394)
Browse files Browse the repository at this point in the history
* feat(authN): Redesign JWT token auth #372

Redesign JWT token authentication middleware to support
additional/alternative authentication method

* claimsfeat(authN): Redesign JWT token auth #372

Split verify function for JWT token auth to make it more readable

---------

Co-authored-by: David Rochow <[email protected]>
  • Loading branch information
michalkrzyz and drochow authored Dec 19, 2024
1 parent 3723c7c commit 2eb43f0
Show file tree
Hide file tree
Showing 13 changed files with 229 additions and 194 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ LOCAL_TEST_DB=true
SEED_MODE=false
```

To enable JWT token authentication, define `AUTH_TOKEN_SECRET` environment variable. Those variable is read by application on startup to start token validation middleware.

### Docker

The `docker-compose.yml` file defines two profiles: `db` for the `heureka-db` service and `heureka` for the `heureka-app` service.
Expand Down
2 changes: 0 additions & 2 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@ services:
DB_NAME: ${DB_NAME}
DB_SCHEMA: /app_sqlschema/schema.sql
SEED_MODE: ${SEED_MODE}
AUTH_TYPE: token
AUTH_TOKEN_SECRET: xxx
volumes:
- ./internal/database/mariadb/init/schema.sql:/app_sqlschema/schema.sql
depends_on:
Expand Down
57 changes: 44 additions & 13 deletions internal/api/graphql/access/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
package access

import (
"strings"
"fmt"
"net/http"
"reflect"

"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
Expand All @@ -17,23 +19,52 @@ type Logger interface {
Warn(...interface{})
}

type Auth interface {
GetMiddleware() gin.HandlerFunc
func NewAuth(cfg *util.Config) *Auth {
l := newLogger()
auth := Auth{logger: l}
auth.AppendInstance(NewTokenAuthMethod(l, cfg))
//TODO: auth.AppendInstance(NewOidcAuthMethod(l, cfg))
return &auth
}

func NewAuth(cfg *util.Config) Auth {
l := newLogger()
type Auth struct {
chain []AuthMethod
logger Logger
}

authType := strings.ToLower(cfg.AuthType)
if authType == "token" {
return NewTokenAuth(l, cfg)
} else if authType == "none" {
return NewNoAuth()
}
type AuthMethod interface {
Verify(*gin.Context) error
}

l.Warn("AUTH_TYPE is not set, assuming 'none' authorization method")
func (a *Auth) GetMiddleware() gin.HandlerFunc {
return func(authCtx *gin.Context) {
if len(a.chain) > 0 {
var retMsg string
for _, auth := range a.chain {
if err := auth.Verify(authCtx); err == nil {
authCtx.Next()
return
} else {
if retMsg != "" {
retMsg = fmt.Sprintf("%s, ", retMsg)
}
retMsg = fmt.Sprintf("%s%s", retMsg, err)
}
}
a.logger.Error("Unauthorized access: %s", retMsg)
authCtx.JSON(http.StatusUnauthorized, gin.H{"error": retMsg})
authCtx.Abort()
return
}
authCtx.Next()
return
}
}

return NewNoAuth()
func (a *Auth) AppendInstance(am AuthMethod) {
if !reflect.ValueOf(am).IsNil() {
a.chain = append(a.chain, am)
}
}

func newLogger() Logger {
Expand Down
21 changes: 0 additions & 21 deletions internal/api/graphql/access/no_auth.go

This file was deleted.

20 changes: 10 additions & 10 deletions internal/api/graphql/access/test/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
)

const (
testUsername = "testUser"
testClientName = "testClientName"
)

func SendGetRequest(url string, headers map[string]string) *http.Response {
Expand Down Expand Up @@ -53,7 +53,7 @@ type Jwt struct {
signingMethod jwt.SigningMethod
signKey interface{}
expiresAt *jwt.NumericDate
username string
name string
}

func NewJwt(secret string) *Jwt {
Expand All @@ -64,8 +64,8 @@ func NewRsaJwt(privKey *rsa.PrivateKey) *Jwt {
return &Jwt{signKey: privKey, signingMethod: jwt.SigningMethodRS256}
}

func (j *Jwt) WithUsername(username string) *Jwt {
j.username = username
func (j *Jwt) WithName(name string) *Jwt {
j.name = name
return j
}

Expand All @@ -81,7 +81,7 @@ func (j *Jwt) String() string {
ExpiresAt: j.expiresAt,
IssuedAt: jwt.NewNumericDate(time.Now()),
Issuer: "heureka",
Subject: j.username,
Subject: j.name,
},
}
token := jwt.NewWithClaims(j.signingMethod, claims)
Expand All @@ -92,15 +92,15 @@ func (j *Jwt) String() string {
}

func GenerateJwt(jwtSecret string, expiresIn time.Duration) string {
return NewJwt(jwtSecret).WithExpiresAt(time.Now().Add(expiresIn)).WithUsername(testUsername).String()
return NewJwt(jwtSecret).WithExpiresAt(time.Now().Add(expiresIn)).WithName(testClientName).String()
}

func GenerateJwtWithUsername(jwtSecret string, expiresIn time.Duration, username string) string {
return NewJwt(jwtSecret).WithExpiresAt(time.Now().Add(expiresIn)).WithUsername(username).String()
func GenerateJwtWithName(jwtSecret string, expiresIn time.Duration, name string) string {
return NewJwt(jwtSecret).WithExpiresAt(time.Now().Add(expiresIn)).WithName(name).String()
}

func GenerateInvalidJwt(jwtSecret string) string {
return NewJwt(jwtSecret).WithUsername(testUsername).String()
return NewJwt(jwtSecret).WithName(testClientName).String()
}

func GenerateRsaPrivateKey() *rsa.PrivateKey {
Expand All @@ -110,5 +110,5 @@ func GenerateRsaPrivateKey() *rsa.PrivateKey {
}

func GenerateJwtWithInvalidSigningMethod(jwtSecret string, expiresIn time.Duration) string {
return NewRsaJwt(GenerateRsaPrivateKey()).WithExpiresAt(time.Now().Add(expiresIn)).WithUsername(testUsername).String()
return NewRsaJwt(GenerateRsaPrivateKey()).WithExpiresAt(time.Now().Add(expiresIn)).WithName(testClientName).String()
}
116 changes: 0 additions & 116 deletions internal/api/graphql/access/token_auth.go

This file was deleted.

Loading

0 comments on commit 2eb43f0

Please sign in to comment.