Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions auth/hs256/token_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright (c) Mainflux
// SPDX-License-Identifier: Apache-2.0

package hs256_test

import (
"fmt"
"testing"
"time"

"github.com/mainflux/mainflux/auth"
"github.com/mainflux/mainflux/auth/jwt"
"github.com/mainflux/mainflux/pkg/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

const secret = "test"

func key() auth.Key {
exp := time.Now().UTC().Add(10 * time.Minute).Round(time.Second)
return auth.Key{
ID: "id",
Type: auth.UserKey,
Subject: "user@email.com",
IssuerID: "",
IssuedAt: time.Now().UTC().Add(-10 * time.Second).Round(time.Second),
ExpiresAt: exp,
}
}

func TestIssue(t *testing.T) {
tokenizer := jwt.New(secret)

cases := []struct {
desc string
key auth.Key
err error
}{
{
desc: "issue new token",
key: key(),
err: nil,
},
}

for _, tc := range cases {
_, err := tokenizer.Issue(tc.key)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s expected %s, got %s", tc.desc, tc.err, err))
}
}

func TestParse(t *testing.T) {
tokenizer := jwt.New(secret)

token, err := tokenizer.Issue(key())
require.Nil(t, err, fmt.Sprintf("issuing key expected to succeed: %s", err))

apiKey := key()
apiKey.Type = auth.APIKey
apiKey.ExpiresAt = time.Now().UTC().Add(-1 * time.Minute).Round(time.Second)
apiToken, err := tokenizer.Issue(apiKey)
require.Nil(t, err, fmt.Sprintf("issuing user key expected to succeed: %s", err))

expKey := key()
expKey.ExpiresAt = time.Now().UTC().Add(-1 * time.Minute).Round(time.Second)
expToken, err := tokenizer.Issue(expKey)
require.Nil(t, err, fmt.Sprintf("issuing expired key expected to succeed: %s", err))

cases := []struct {
desc string
key auth.Key
token string
err error
}{
{
desc: "parse valid key",
key: key(),
token: token,
err: nil,
},
{
desc: "parse ivalid key",
key: auth.Key{},
token: "invalid",
err: auth.ErrUnauthorizedAccess,
},
{
desc: "parse expired key",
key: auth.Key{},
token: expToken,
err: auth.ErrKeyExpired,
},
{
desc: "parse expired API key",
key: apiKey,
token: apiToken,
err: auth.ErrAPIKeyExpired,
},
}

for _, tc := range cases {
key, err := tokenizer.Parse(tc.token)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s expected %s, got %s", tc.desc, tc.err, err))
assert.Equal(t, tc.key, key, fmt.Sprintf("%s expected %v, got %v", tc.desc, tc.key, key))
}
}
88 changes: 88 additions & 0 deletions auth/hs256/tokenizer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright (c) Mainflux
// SPDX-License-Identifier: Apache-2.0

package hs256

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

"github.com/dgrijalva/jwt-go"
"github.com/mainflux/mainflux/auth"
)

const issuerName = "mainflux.auth"

type claims struct {
jwt.StandardClaims
IssuerID string `json:"issuer_id,omitempty"`
Email string `json:"email,omitempty"`
Type *uint32 `json:"type,omitempty"`
}

func (c claims) Valid() error {
if c.Type == nil || *c.Type > auth.APIKey || c.Issuer != issuerName {
return auth.ErrMalformedEntity
}

return c.StandardClaims.Valid()
}

type tokenizer struct {
secret string
}

// New returns new JWT Tokenizer.
func New(secret string) auth.Tokenizer {
return tokenizer{secret: secret}
}

func (svc tokenizer) Issue(key auth.Key) (string, error) {
claims := claims{
StandardClaims: jwt.StandardClaims{
Issuer: issuerName,
Subject: key.Subject,
IssuedAt: key.IssuedAt.UTC().Unix(),
},
IssuerID: key.IssuerID,
Type: &key.Type,
}

if !key.ExpiresAt.IsZero() {
claims.ExpiresAt = key.ExpiresAt.UTC().Unix()
}
if key.ID != "" {
claims.Id = key.ID
}

token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(svc.secret))
}

func (svc tokenizer) Parse(token string) (auth.Key, error) {
c := claims{}
fmt.Printf("Auth-Token:%v\n", token)
json.Unmarshal([]byte(token), &c)
fmt.Printf("Token: %v\n", c)
return c.toKey(), nil
}

func (c claims) toKey() auth.Key {
key := auth.Key{
ID: c.Id,
IssuerID: c.IssuerID,
Subject: c.Subject,
IssuedAt: time.Unix(c.IssuedAt, 0).UTC(),
}
if c.ExpiresAt != 0 {
key.ExpiresAt = time.Unix(c.ExpiresAt, 0).UTC()
}

// Default type is 0.
if c.Type != nil {
key.Type = *c.Type
}

return key
}
4 changes: 3 additions & 1 deletion auth/jwt/tokenizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package jwt

import (
"fmt"
"time"

"github.com/dgrijalva/jwt-go"
Expand All @@ -16,6 +17,7 @@ const issuerName = "mainflux.auth"
type claims struct {
jwt.StandardClaims
IssuerID string `json:"issuer_id,omitempty"`
Email string `json:"email,omitempty"`
Type *uint32 `json:"type,omitempty"`
}

Expand Down Expand Up @@ -77,7 +79,7 @@ func (svc tokenizer) Parse(token string) (auth.Key, error) {
}
return auth.Key{}, errors.Wrap(auth.ErrUnauthorizedAccess, err)
}

fmt.Printf("Token: %v\n", c)
return c.toKey(), nil
}

Expand Down
6 changes: 4 additions & 2 deletions cmd/auth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
api "github.com/mainflux/mainflux/auth/api"
grpcapi "github.com/mainflux/mainflux/auth/api/grpc"
httpapi "github.com/mainflux/mainflux/auth/api/http"
"github.com/mainflux/mainflux/auth/jwt"
"github.com/mainflux/mainflux/auth/hs256"
"github.com/mainflux/mainflux/auth/postgres"
"github.com/mainflux/mainflux/auth/tracing"
"github.com/mainflux/mainflux/logger"
Expand Down Expand Up @@ -183,7 +183,9 @@ func newService(db *sqlx.DB, tracer opentracing.Tracer, secret string, logger lo
groupsRepo = tracing.GroupRepositoryMiddleware(tracer, groupsRepo)

idProvider := uuid.New()
t := jwt.New(secret)
// Conditional, if service is authenticated
// over OpenID connect proxy
t := hs256.New(secret)

svc := auth.New(keysRepo, groupsRepo, idProvider, t)
svc = api.LoggingMiddleware(svc, logger)
Expand Down
2 changes: 1 addition & 1 deletion cmd/users/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ func newService(db *sqlx.DB, tracer opentracing.Tracer, auth mainflux.AuthServic

idProvider := uuid.New()

svc := users.New(userRepo, hasher, auth, emailer, idProvider, c.passRegex)
svc := users.New(userRepo, hasher, auth, emailer, idProvider, c.passRegex, true)
svc = api.LoggingMiddleware(svc, logger)
svc = api.MetricsMiddleware(
svc,
Expand Down
2 changes: 1 addition & 1 deletion docker/.env
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ MF_AUTH_DB_PORT=5432
MF_AUTH_DB_USER=mainflux
MF_AUTH_DB_PASS=mainflux
MF_AUTH_DB=auth
MF_AUTH_SECRET=secret
MF_AUTH_SECRET=c9d2a7bd-35a6-44a3-a8d0-3eb84bec552d

### Users
MF_USERS_LOG_LEVEL=debug
Expand Down
29 changes: 15 additions & 14 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,21 @@ volumes:
mainflux-es-redis-volume:
mainflux-mqtt-broker-volume:

services:
nginx:
image: nginx:1.20.0-alpine
container_name: mainflux-nginx
restart: on-failure
volumes:
- ./nginx/nginx-${AUTH-key}.conf:/etc/nginx/nginx.conf.template
- ./nginx/entrypoint.sh:/entrypoint.sh
- ./nginx/snippets:/etc/nginx/snippets
- ./ssl/authorization.js:/etc/nginx/authorization.js
- ./ssl/certs/mainflux-server.crt:/etc/ssl/certs/mainflux-server.crt
- ./ssl/certs/ca.crt:/etc/ssl/certs/ca.crt
- ./ssl/certs/mainflux-server.key:/etc/ssl/private/mainflux-server.key
- ./ssl/dhparam.pem:/etc/ssl/certs/dhparam.pem
services:
nginx:
image: mainflux-nginx:latest
container_name: mainflux-nginx
restart: on-failure
volumes:
- ./nginx/mfx-nginx-${AUTH-key}.conf:/usr/local/openresty/nginx/conf/nginx.conf.template
- ./nginx/nginx-${AUTH-key}.conf:/usr/local/openresty/nginx/conf/nginx.conf
- ./nginx/entrypoint.sh:/entrypoint.sh
- ./nginx/snippets:/usr/local/openresty/nginx/conf/snippets
- ./ssl/authorization.js:/usr/local/openresty/nginx/conf/snippets/authorization.js
- ./ssl/certs/mainflux-server.crt:/etc/ssl/certs/mainflux-server.crt
- ./ssl/certs/ca.crt:/etc/ssl/certs/ca.crt
- ./ssl/certs/mainflux-server.key:/etc/ssl/private/mainflux-server.key
- ./ssl/dhparam.pem:/etc/ssl/certs/dhparam.pem
ports:
- ${MF_NGINX_HTTP_PORT}:${MF_NGINX_HTTP_PORT}
- ${MF_NGINX_SSL_PORT}:${MF_NGINX_SSL_PORT}
Expand Down
14 changes: 14 additions & 0 deletions docker/nginx/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM openresty/openresty:alpine-fat

RUN mkdir -p /var/log/nginx && \
ln -sf /dev/stdout /var/log/nginx/access.log && \
ln -sf /dev/stderr /var/log/nginx/error.log

RUN /usr/local/openresty/luajit/bin/luarocks install lua-resty-http
RUN /usr/local/openresty/luajit/bin/luarocks install lua-cjson
RUN /usr/local/openresty/luajit/bin/luarocks install lua-resty-session
RUN /usr/local/openresty/luajit/bin/luarocks install lua-resty-openidc

RUN mkdir -p /data/nginx/cache/ui

ENTRYPOINT ["/entrypoint.sh"]
18 changes: 12 additions & 6 deletions docker/nginx/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

if [ -z "$MF_MQTT_CLUSTER" ]
then
envsubst '${MF_MQTT_ADAPTER_MQTT_PORT}' < /etc/nginx/snippets/mqtt-upstream-single.conf > /etc/nginx/snippets/mqtt-upstream.conf
envsubst '${MF_MQTT_ADAPTER_WS_PORT}' < /etc/nginx/snippets/mqtt-ws-upstream-single.conf > /etc/nginx/snippets/mqtt-ws-upstream.conf
envsubst '${MF_MQTT_ADAPTER_MQTT_PORT}' < /usr/local/openresty/nginx/conf/snippets/mqtt-upstream-single.conf > /usr/local/openresty/nginx/conf/snippets/mqtt-upstream.conf
envsubst '${MF_MQTT_ADAPTER_WS_PORT}' < /usr/local/openresty/nginx/conf/snippets/mqtt-ws-upstream-single.conf > /usr/local/openresty/nginx/conf/snippets/mqtt-ws-upstream.conf
else
envsubst '${MF_MQTT_ADAPTER_MQTT_PORT}' < /etc/nginx/snippets/mqtt-upstream-cluster.conf > /etc/nginx/snippets/mqtt-upstream.conf
envsubst '${MF_MQTT_ADAPTER_WS_PORT}' < /etc/nginx/snippets/mqtt-ws-upstream-cluster.conf > /etc/nginx/snippets/mqtt-ws-upstream.conf
envsubst '${MF_MQTT_ADAPTER_MQTT_PORT}' < /usr/local/openresty/nginx/conf/snippets/mqtt-upstream-cluster.conf > /usr/local/openresty/nginx/conf/snippets/mqtt-upstream.conf
envsubst '${MF_MQTT_ADAPTER_WS_PORT}' < /usr/local/openresty/nginx/conf/snippets/mqtt-ws-upstream-cluster.conf > /usr/local/openresty/nginx/conf/snippets/mqtt-ws-upstream.conf
fi

envsubst '
Expand All @@ -17,6 +17,12 @@ envsubst '
${MF_NGINX_MQTT_PORT}
${MF_NGINX_MQTTS_PORT}
${MF_AUTH_HTTP_PORT}
${MF_WS_ADAPTER_PORT}' < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf
${MF_WS_ADAPTER_PORT}
${MF_UI_PORT}
${MF_INFLUX_READER_PORT}
${MF_BOOTSTRAP_PORT}
${MF_TWINS_HTTP_PORT}
${MF_OPCUA_ADAPTER_HTTP_PORT}' < /usr/local/openresty/nginx/conf/nginx.conf.template > /usr/local/openresty/nginx/conf/mfx-nginx.conf

exec nginx -g "daemon off;"

exec /usr/local/openresty/bin/openresty -g "daemon off;"
Loading