Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JWT Feature #367

Merged
merged 92 commits into from
Jun 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
92 commits
Select commit Hold shift + click to select a range
9e135df
JWT as a middleware authenticator
AbineshECAD Apr 20, 2023
cc597de
Merge branch 'main' of github.com:ecadlabs/signatory into jwt-auth
AbineshECAD Apr 20, 2023
056b059
Nil check for JWT
AbineshECAD Apr 21, 2023
d2791d4
user credentials verification
AbineshECAD Apr 21, 2023
c8b0c0c
Integration test for JWT
AbineshECAD Apr 25, 2023
1096b04
signature invalid issue fixed
AbineshECAD Apr 25, 2023
c839d66
Removed unwanted debug changes
AbineshECAD Apr 25, 2023
9d4c6a4
Merge branch 'main' of github.com:ecadlabs/signatory into jwt-auth
AbineshECAD Apr 25, 2023
d702b45
Removes debug prints
AbineshECAD Apr 27, 2023
d0a605b
test code refactor
AbineshECAD Apr 27, 2023
2af5489
Documentation for JWT
AbineshECAD Apr 28, 2023
30ac094
Doc page
AbineshECAD Apr 28, 2023
ff045e6
Spelling correction
AbineshECAD Apr 28, 2023
8216219
Doc corrections
AbineshECAD Apr 28, 2023
5600d42
Spell correction
AbineshECAD Apr 28, 2023
f896104
Spell correction
AbineshECAD May 1, 2023
2ea1d0a
config read issues fixed
AbineshECAD May 1, 2023
468f4cc
Sample config added to document
AbineshECAD May 2, 2023
9918bb3
Doc correction
AbineshECAD May 2, 2023
639aabc
Spell corrections
AbineshECAD May 2, 2023
a7be592
Client authorization sidebar change in docs
AbineshECAD May 2, 2023
bd32d8d
Merge branch 'main' of github.com:ecadlabs/signatory into jwt-auth
AbineshECAD May 2, 2023
3a2a7fb
Client authorization sidebar change in docs
AbineshECAD May 2, 2023
ed50924
Client authorization sidebar change in docs
AbineshECAD May 2, 2023
9f414ec
SidebarHeader code alignment change
AbineshECAD May 2, 2023
6b7c318
token expiry optional
AbineshECAD May 3, 2023
ed1e3c7
token expiry description added
AbineshECAD May 3, 2023
1a0054f
doc update on tok_expiry
AbineshECAD May 3, 2023
32d8277
Merge branch 'main' into jwt-auth
May 6, 2023
7ce7ef8
minor changes as per discussion
AbineshECAD May 8, 2023
d3cd9b7
doc tab corrections
AbineshECAD May 8, 2023
158baef
tok_exp changes as per discussion
AbineshECAD May 8, 2023
a4399a6
minor variable name changes
AbineshECAD May 8, 2023
a3cb19e
credentials check logic updated
AbineshECAD May 8, 2023
92a5cb5
sg add an integration test for jwt
May 9, 2023
be20e33
login endpoint added
AbineshECAD May 9, 2023
3d44d09
login serve issue fix
AbineshECAD May 9, 2023
cc333a8
fix jwt integration test
May 9, 2023
0c3f4d4
Merge branch 'jwt-auth' of github.com:ecadlabs/signatory into jwt-auth
May 9, 2023
05e09f9
add more jwt tests
May 11, 2023
f0a8b4b
username and password error messages match
AbineshECAD May 11, 2023
c9ddde0
Merge branch 'jwt-auth' of github.com:ecadlabs/signatory into jwt-auth
AbineshECAD May 11, 2023
5e2f25b
unit test case for Login handler
AbineshECAD May 11, 2023
7152554
AuthHandler UT
AbineshECAD May 16, 2023
648e107
Merge branch 'main' of github.com:ecadlabs/signatory into jwt-auth
AbineshECAD May 16, 2023
dff45b5
Merge branch 'main' of github.com:ecadlabs/signatory into jwt-auth
AbineshECAD May 16, 2023
26f32ac
JWT and authorized_keys are mutually exclusive, can't use both
AbineshECAD May 17, 2023
8d8fee0
Merge branch 'main' of github.com:ecadlabs/signatory into jwt-pkh
AbineshECAD May 19, 2023
e436553
sg-integrationtest add bad input test
May 19, 2023
bc9f804
JWT users per PKH & credentials rotation
AbineshECAD May 26, 2023
20ec62a
Merge branch 'jwt-auth' of github.com:ecadlabs/signatory into jwt-pkh
AbineshECAD May 26, 2023
cd37f68
Merge branch 'main' into jwt-pkh
AbineshECAD May 26, 2023
5d50280
Unit tests
AbineshECAD May 29, 2023
b66491d
Merge branch 'jwt-pkh' of github.com:ecadlabs/signatory into jwt-pkh
AbineshECAD May 29, 2023
8a7e34f
Merge branch 'main' into jwt-pkh
AbineshECAD May 29, 2023
ea956c5
nil check for jwt users list
AbineshECAD May 29, 2023
98504d6
UT refactor
AbineshECAD May 30, 2023
b65ea4e
fix integration test
May 30, 2023
579e012
per PKH config change
AbineshECAD May 31, 2023
4bdf0db
Merge branch 'jwt-pkh' of github.com:ecadlabs/signatory into jwt-pkh
AbineshECAD May 31, 2023
81310ef
error messages got JWT label
AbineshECAD May 31, 2023
ddc6c30
secert validation changes
AbineshECAD Jun 1, 2023
d1871f9
Merge branch 'main' into jwt-pkh
stephengaudet Jun 1, 2023
48ed00f
fix jwt unit test new secret length constraint
Jun 1, 2023
8def897
Merge branch 'jwt-pkh' of github.com:ecadlabs/signatory into jwt-pkh
Jun 1, 2023
68a5669
fix jwt unit test new secret length constraint
Jun 1, 2023
3ff222d
fix jwt unit test
Jun 1, 2023
5161bbf
fix integration test - new secret constraint
Jun 1, 2023
7d40207
add (failing) jwt password rotation integration test
Jun 2, 2023
6f5cea4
secret & password validation changes
AbineshECAD Jun 2, 2023
fb2a881
Merge branch 'jwt-pkh' of github.com:ecadlabs/signatory into jwt-pkh
AbineshECAD Jun 2, 2023
f66ad21
add jwt per pkh integration tests
Jun 2, 2023
2135014
Merge branch 'jwt-pkh' of github.com:ecadlabs/signatory into jwt-pkh
Jun 2, 2023
0b286d7
new and old user cred accepted in parallel till the exp time
AbineshECAD Jun 5, 2023
fa4f16b
integration test - add password complexity 16 characters
Jun 5, 2023
9ed10d8
integrationtest - fix password rotation testcase - requires new secret
Jun 5, 2023
f4ff543
fix for issue #372
AbineshECAD Jun 7, 2023
1a9710f
Merge branch 'jwt-pkh' of github.com:ecadlabs/signatory into jwt-pkh
AbineshECAD Jun 7, 2023
c38642d
error message added
AbineshECAD Jun 7, 2023
1dd0b12
fix broken test
Jun 7, 2023
ecae315
fix broken test
Jun 7, 2023
1e884a7
Merge branch 'main' into jwt-pkh
AbineshECAD Jun 8, 2023
505d0e5
old expiry logic change
AbineshECAD Jun 12, 2023
b723457
Merge branch 'jwt-pkh' of github.com:ecadlabs/signatory into jwt-pkh
AbineshECAD Jun 12, 2023
d9fd0c8
fix password rotation integration test
Jun 12, 2023
e10a69e
tidy test
Jun 12, 2023
1201637
small jwt doc improvements
Jun 12, 2023
0c1b3f6
jwt doc fix typo
Jun 12, 2023
80f1901
bump octez version in integration tests
Jun 13, 2023
75262fa
Merge branch 'main' into jwt-pkh
Jun 13, 2023
ffa6a39
Merge branch 'main' into jwt-pkh
Jun 13, 2023
ff36477
update octez version in integration tests
Jun 13, 2023
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
13 changes: 13 additions & 0 deletions cmd/commands/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package commands

import (
"context"
"fmt"
"net/http"
"time"

"github.com/ecadlabs/signatory/pkg/auth"
"github.com/ecadlabs/signatory/pkg/middlewares"
"github.com/ecadlabs/signatory/pkg/server"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
Expand All @@ -24,6 +26,17 @@ func NewServeCommand(c *Context) *cobra.Command {
Signer: c.signatory,
}

if c.config.Server.JWTConfig != nil {
if c.config.Server.AuthorizedKeys != nil {
return fmt.Errorf("cannot use both JWT and static authorized keys")
}
mw := middlewares.NewMiddleware(c.config.Server.JWTConfig)
if err := c.config.Server.JWTConfig.CheckUpdateNewCred(); err != nil {
return err
}
srvConf.MidWare = mw
}

if c.config.Server.AuthorizedKeys != nil {
ak, err := auth.StaticAuthorizedKeys(c.config.Server.AuthorizedKeys.List()...)
if err != nil {
Expand Down
163 changes: 163 additions & 0 deletions docs/jwt_auth.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
---
id: jwt
title: JWT
---

# JWT - Signatory interaction

The diagram represents a sequence of interactions between a client and a Signatory service, which appears to be a service that handles cryptographic signing operations. Here is a breakdown of the sequence of interactions:

```mermaid
sequenceDiagram
participant Client
participant Signatory
Client->>Signatory: Provide credentials
Signatory->>Client: Verify credentials
Note over Signatory: Create JWT
Signatory->>Client: Send JWT
Note over Client: Store JWT
Client->>Signatory: Request signing operation (include JWT)
Signatory->>Signatory: Validate JWT signature
Note over Signatory: Check claims (public key, roles, permissions)
alt JWT is valid and client is authorized
Signatory->>Signatory: Sign operation with private key
Signatory->>Client: Send signed operation
else JWT is invalid or client is unauthorized
Signatory->>Client: Access denied (error message)
end
```

1. The client provides its credentials to the Signatory service.
2. The Signatory service verifies the credentials provided by the client.
3. If the credentials are valid, the Signatory service creates a JSON Web Token (JWT) and sends it to the client.
4. The client stores the JWT for later use.
5. The client requests a signing operation from the Signatory service, and includes the JWT in the request.
6. The Signatory service validates the JWT signature and checks the claims in the JWT (such as the public key, roles, and permissions).
7. If the JWT is valid and the client is authorized, the Signatory service signs the operation with its private key and sends the signed operation back to the client.
8. If the JWT is invalid or the client is unauthorized, the Signatory service sends an access denied error message to the client.
9. When the token expires or any other token authentication failures happen, the client can start again from 1.

## Sample Signatory JWT configuration

`jwt_exp` is the time duration (in minutes) for which the token is valid and it is optional. When not provided the token expiry is 60 minutes, otherwise it is `current time + jwt_exp`.

`secret` is the secret used to sign the JWT token.

```yaml
server:
address: :6732
utility_address: :9583
jwt:
users:
user_name1:
password: password1
secret: secret1
jwt_exp: 234
user_name2:
password: password2
secret: secret2
jwt_exp: 73
```

## Sample client code which used in the integration test.

```go
//Send user credentials to receive a new token
url := "http://localhost:6732/login" + pub.Hash().String()
client := &http.Client{}

req, err := http.NewRequest("POST", url, nil)
require.NoError(t, err)

req.Header.Add("Content-Type", "application/json")
req.Header.Add("username", "user1")
req.Header.Add("password", "pass123")
time.Sleep(2 * time.Second)

res, err := client.Do(req)
require.NoError(t, err)
require.Equal(t, 201, res.StatusCode)

body, err := ioutil.ReadAll(res.Body)
require.NoError(t, err)
require.NotEmpty(t, body)

res.Body.Close()

// Send request using received token

client = &http.Client{}

req, err = http.NewRequest("GET", url, nil)
require.NoError(t, err)

req.Header.Add("Content-Type", "application/json")
req.Header.Add("Authorization", "Bearer "+string(body))
req.Header.Add("username", "user1")
time.Sleep(2 * time.Second)

res, err = client.Do(req)
require.NoError(t, err)
require.Equal(t, 200, res.StatusCode)

defer res.Body.Close()
body, err = ioutil.ReadAll(res.Body)
require.NoError(t, err)
require.NotEmpty(t, body)

fmt.Println("Response-GET-PKH: ", string(body))
```

## Credentials rotation

The credentials can be rotated by updating the configuration file and restarting the Signatory service.

Older credentials can be removed from the configuration file after the new credentials are added and signatory is up and serving. The Signatory service will continue to accept the older credentials until the `old_cred_exp` time expires. If any error occurs with expiry time, the Signatory service will stop accepting the older credentials immediately. The `old_cred_exp` field is `GMT` expressed in `YYYY-MM-DD hh:mm:ss` format.

### sample configuration file:

```yaml
server:
address: :6732
utility_address: :9583
jwt:
users:
user_name1:
password: password1
secret: secret1
jwt_exp: 234
old_cred_exp: "2006-01-02 15:04:05"
new_data:
password: password1
secret: secret1
jwt_exp: 35
```

## JWT users for each PKH

The JWT users can be configured for each PKH in the configuration file. Even if the JWT client provides a valid token, the request will be rejected if the user is not configured for that PKH requested for signing.
If no JWT users are configured for a PKH, then any JWT token will be accepted for that PKH.

### sample configuration file:

```yaml
tezos:
tz3cbDCwrqFqfx1dBhHoXTwZ9FG3MDrtyMs6:
jwt_users:
- user_name1
- user_name2
log_payloads: true
allow:
block:
endorsement:
preendorsement:
generic:
- transaction
- reveal
```

## Important security note:

TLS should be taken care by the user who configures JWT as the authentication mechanism in Signatory for the clients.

The configuration file also contains sensitive information when using JWT with Signatory, so that file must also be secure.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ require (
github.com/enceve/crypto v0.0.0-20160707101852-34d48bb93815 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/golang-jwt/jwt/v5 v5.0.0-rc.1
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU=
github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v5 v5.0.0-rc.1 h1:tDQ1LjKga657layZ4JLsRdxgvupebc0xuPwRNuTfUgs=
github.com/golang-jwt/jwt/v5 v5.0.0-rc.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
Expand Down
2 changes: 1 addition & 1 deletion integration_test/.env.current
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export OCTEZ_VERSION=${ARCH}_v16.0-rc3
export OCTEZ_VERSION=${ARCH}_v16.1
export PROTOCOL=Mumbai
2 changes: 1 addition & 1 deletion integration_test/.env.next
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export OCTEZ_VERSION=${ARCH}_v17.0-beta1
export OCTEZ_VERSION=${ARCH}_v17.0
export PROTOCOL=Nairobi
26 changes: 23 additions & 3 deletions integration_test/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,37 @@ type Config struct {
Tezos TezosConfig `yaml:"tezos"`
}

type JwtConfig struct {
Users map[string]*JwtUserData `yaml:"users"`
}

type JwtUserData struct {
Password string `yaml:"password"`
Secret string `yaml:"secret"`
Exp uint64 `yaml:"jwt_exp"`
CredExp string `yaml:"old_cred_exp,omitempty"`
NewCred *JwtNewCred `yaml:"new_data,omitempty"`
}

type JwtNewCred struct {
Password string `yaml:"password"`
Secret string `yaml:"secret"`
Exp uint64 `yaml:"jwt_exp"`
}

type ServerConfig struct {
Address string `yaml:"address"`
UtilityAddress string `yaml:"utility_address"`
Keys []string `yaml:"authorized_keys,omitempty"`
Address string `yaml:"address"`
UtilityAddress string `yaml:"utility_address"`
Keys []string `yaml:"authorized_keys,omitempty"`
Jwt JwtConfig `yaml:"jwt,omitempty"`
}

type TezosConfig = map[string]*TezosPolicy

type TezosPolicy struct {
Allow map[string][]string `yaml:"allow"`
LogPayloads bool `yaml:"log_payloads"`
JwtUsers []string `yaml:"jwt_users,omitempty"`
}

type VaultConfig struct {
Expand Down
Loading