|
| 1 | +--- |
| 2 | +id: jwt |
| 3 | +title: JWT |
| 4 | +--- |
| 5 | + |
| 6 | +# JWT - Signatory interaction |
| 7 | + |
| 8 | +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: |
| 9 | + |
| 10 | +```mermaid |
| 11 | +sequenceDiagram |
| 12 | + participant Client |
| 13 | + participant Signatory |
| 14 | + Client->>Signatory: Provide credentials |
| 15 | + Signatory->>Client: Verify credentials |
| 16 | + Note over Signatory: Create JWT |
| 17 | + Signatory->>Client: Send JWT |
| 18 | + Note over Client: Store JWT |
| 19 | + Client->>Signatory: Request signing operation (include JWT) |
| 20 | + Signatory->>Signatory: Validate JWT signature |
| 21 | + Note over Signatory: Check claims (public key, roles, permissions) |
| 22 | + alt JWT is valid and client is authorized |
| 23 | + Signatory->>Signatory: Sign operation with private key |
| 24 | + Signatory->>Client: Send signed operation |
| 25 | + else JWT is invalid or client is unauthorized |
| 26 | + Signatory->>Client: Access denied (error message) |
| 27 | + end |
| 28 | +``` |
| 29 | + |
| 30 | +1. The client provides its credentials to the Signatory service. |
| 31 | +2. The Signatory service verifies the credentials provided by the client. |
| 32 | +3. If the credentials are valid, the Signatory service creates a JSON Web Token (JWT) and sends it to the client. |
| 33 | +4. The client stores the JWT for later use. |
| 34 | +5. The client requests a signing operation from the Signatory service, and includes the JWT in the request. |
| 35 | +6. The Signatory service validates the JWT signature and checks the claims in the JWT (such as the public key, roles, and permissions). |
| 36 | +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. |
| 37 | +8. If the JWT is invalid or the client is unauthorized, the Signatory service sends an access denied error message to the client. |
| 38 | +9. When the token expires or any other token authentication failures happen, the client can start again from 1. |
| 39 | + |
| 40 | +## Sample Signatory JWT configuration |
| 41 | + |
| 42 | +`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`. |
| 43 | + |
| 44 | +`secret` is the secret used to sign the JWT token. |
| 45 | + |
| 46 | +```yaml |
| 47 | +server: |
| 48 | + address: :6732 |
| 49 | + utility_address: :9583 |
| 50 | + jwt: |
| 51 | + users: |
| 52 | + user_name1: |
| 53 | + password: password1 |
| 54 | + secret: secret1 |
| 55 | + jwt_exp: 234 |
| 56 | + user_name2: |
| 57 | + password: password2 |
| 58 | + secret: secret2 |
| 59 | + jwt_exp: 73 |
| 60 | +``` |
| 61 | +
|
| 62 | +## Sample client code which used in the integration test. |
| 63 | +
|
| 64 | +```go |
| 65 | + //Send user credentials to receive a new token |
| 66 | + url := "http://localhost:6732/login" + pub.Hash().String() |
| 67 | + client := &http.Client{} |
| 68 | + |
| 69 | + req, err := http.NewRequest("POST", url, nil) |
| 70 | + require.NoError(t, err) |
| 71 | + |
| 72 | + req.Header.Add("Content-Type", "application/json") |
| 73 | + req.Header.Add("username", "user1") |
| 74 | + req.Header.Add("password", "pass123") |
| 75 | + time.Sleep(2 * time.Second) |
| 76 | + |
| 77 | + res, err := client.Do(req) |
| 78 | + require.NoError(t, err) |
| 79 | + require.Equal(t, 201, res.StatusCode) |
| 80 | + |
| 81 | + body, err := ioutil.ReadAll(res.Body) |
| 82 | + require.NoError(t, err) |
| 83 | + require.NotEmpty(t, body) |
| 84 | + |
| 85 | + res.Body.Close() |
| 86 | + |
| 87 | + // Send request using received token |
| 88 | + |
| 89 | + client = &http.Client{} |
| 90 | + |
| 91 | + req, err = http.NewRequest("GET", url, nil) |
| 92 | + require.NoError(t, err) |
| 93 | + |
| 94 | + req.Header.Add("Content-Type", "application/json") |
| 95 | + req.Header.Add("Authorization", "Bearer "+string(body)) |
| 96 | + req.Header.Add("username", "user1") |
| 97 | + time.Sleep(2 * time.Second) |
| 98 | + |
| 99 | + res, err = client.Do(req) |
| 100 | + require.NoError(t, err) |
| 101 | + require.Equal(t, 200, res.StatusCode) |
| 102 | + |
| 103 | + defer res.Body.Close() |
| 104 | + body, err = ioutil.ReadAll(res.Body) |
| 105 | + require.NoError(t, err) |
| 106 | + require.NotEmpty(t, body) |
| 107 | + |
| 108 | + fmt.Println("Response-GET-PKH: ", string(body)) |
| 109 | +``` |
| 110 | + |
| 111 | +## Credentials rotation |
| 112 | + |
| 113 | +The credentials can be rotated by updating the configuration file and restarting the Signatory service. |
| 114 | + |
| 115 | +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. |
| 116 | + |
| 117 | +### sample configuration file: |
| 118 | + |
| 119 | +```yaml |
| 120 | +server: |
| 121 | + address: :6732 |
| 122 | + utility_address: :9583 |
| 123 | + jwt: |
| 124 | + users: |
| 125 | + user_name1: |
| 126 | + password: password1 |
| 127 | + secret: secret1 |
| 128 | + jwt_exp: 234 |
| 129 | + old_cred_exp: "2006-01-02 15:04:05" |
| 130 | + new_data: |
| 131 | + password: password1 |
| 132 | + secret: secret1 |
| 133 | + jwt_exp: 35 |
| 134 | +``` |
| 135 | +
|
| 136 | +## JWT users for each PKH |
| 137 | +
|
| 138 | +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. |
| 139 | +If no JWT users are configured for a PKH, then any JWT token will be accepted for that PKH. |
| 140 | +
|
| 141 | +### sample configuration file: |
| 142 | +
|
| 143 | +```yaml |
| 144 | +tezos: |
| 145 | + tz3cbDCwrqFqfx1dBhHoXTwZ9FG3MDrtyMs6: |
| 146 | + jwt_users: |
| 147 | + - user_name1 |
| 148 | + - user_name2 |
| 149 | + log_payloads: true |
| 150 | + allow: |
| 151 | + block: |
| 152 | + endorsement: |
| 153 | + preendorsement: |
| 154 | + generic: |
| 155 | + - transaction |
| 156 | + - reveal |
| 157 | +``` |
| 158 | +
|
| 159 | +## Important security note: |
| 160 | +
|
| 161 | +TLS should be taken care by the user who configures JWT as the authentication mechanism in Signatory for the clients. |
| 162 | +
|
| 163 | +The configuration file also contains sensitive information when using JWT with Signatory, so that file must also be secure. |
0 commit comments