1# JWT support in lws 2 3lws supports the common usage scenarios of JWS (signed) JWT generation, 4parsing and transferring in and out as http cookies. Care is taken to provide 5helpers that implement the current security best practices for cookie handling 6and JWT validation. All of the common algorithms like ES512 are supported 7along with JWK generation and handling apis. 8 9The build options needed are `-DLWS_WITH_JOSE=1` `-DLWS_WITH_GENCRYPTO=1`. 10 11Underlying JOSE primitives are exposed as apis, some JWT specific primitives 12and finally a JWT-via http cookie level creation apis each building on top of 13what was underneath. 14 15The higher level APIs are provided additionally because they have the most 16opportunity for implementation pitfalls like not validating alg carefully, or 17not using the latest cookie security options; the provided APIs handle that 18centrally for you. If your needs vary from what the higher level apis are 19doing, you can cut-and-paste out those implementations and create your own 20using the public lower level apis. 21 22## LWS JWT fields 23 24Lws JWT uses mainly well-known fields 25 26Field|Std|Meaning 27---|---|--- 28iss|yes|Issuer, typically the domain like "warmcat.com" 29aud|yes|Audience, typically a url path like "https://warmcat.com/sai" 30iat|yes|Unix-time "Issued At" 31nbf|yes|Unix-time "Not Before" 32exp|yes|Unix-time "Expired" 33sub|yes|Subject, eg, a username or user email 34csrf|no|A random 16-char hex token generated with the JWT for use in links specific to the JWT bearer 35ext|no|Application-specific JSON sub-object with whatever fields you need, eg, `"authorization": 1` 36 37## Approach for JWT as session token 38 39Once JWTs are produced, they are autonomous bearer tokens, if they are not kept 40secret between the browser and the site, they will be accepted as evidence for 41having rights to the session from anyone. 42 43Requiring https, and various other cookie hardening techniques make it more 44difficult for them to leak, but it is still necessary to strictly constrain the 45token's validity time, usually to a few tens of minutes or how long it takes a 46user to login and get stuff done on the site in one session. 47 48## CSRF mitigation 49 50Cross Site Request Forgery (CSRF) is a hacking scenario where an authorized 51user with a valid token is tricked into clicking on an external link that 52performs some action with side-effects on the site he has active auth on. For 53example, he has a cookie that's logged into his bank, and the link posts a form 54to the bank site transferring money to the attacker. 55 56Lws JWT mitigates this possibility by putting a random secret in the generated 57JWT; when the authorized user presents his JWT to generate the page, generated 58links that require auth to perform their actions include the CSRF string from 59that user's current JWT. 60 61When the user clicks those links intentionally, the CSRF string in the link 62matches the CSRF string in the currently valid JWT that was also provided to 63the server along with the click, and all is well. 64 65An attacker does not know the random, ephemeral JWT CSRF secret to include in 66forged links, so the attacker-controlled action gets rejected at the server as 67having used an invalid link. 68 69The checking and link manipulation need to be implemented in user code / JS... 70lws JWT provides the random CSRF secret in the JWT and makes it visible to the 71server when the incoming JWT is processed. 72 73## Need for client tracking of short JWT validity times 74 75Many links or references on pages do not require CSRF strings, only those that 76perform actions with side-effects like deletion or money transfer should need 77protecting this way. 78 79Due to CSRF mitigation, generated pages containing the protected links 80effectively have an expiry time linked to that of the JWT, since only the bearer 81of the JWT used to generate the links on the page can use them; once that 82expires actually nobody can use them and the page contents, which may anyway 83be showing content that only authenticated users can see must be invalidated and 84re-fetched. Even if the contents are visible without authentication, additional 85UI elements like delete buttons that should only be shown when authenticated 86will wrongly still be shown 87 88For that reason, the client should be informed by the server along with the 89authentication status, the expiry time of it. The client should then by itself 90make arrangements to refresh the page when this time is passed, 91either showing an unauthenticated version of the same page if it exists, or by 92redirecting to the site homepage if showing any of the contents required 93authentication. The user can then log back in using his credientials typically 94stored in the browser's password store and receive a new short-term JWT with a 95new random csrf token along with a new page using the new csrf token in its 96links. 97 98## Considerations for long-lived connections 99 100Once established as authorized, websocket links may be very long-lived and hold 101their authorization state at the server. Although the browser monitoring the 102JWT reloading the page on auth expiry should mitigate this, an attacker can 103choose to just not do that and have an immortally useful websocket link. 104 105At least for actions on the long-lived connection, it should not only confirm 106the JWT authorized it but that the current time is still before the "exp" time 107in the JWT, this is made available as `expiry_unix_time` in the args struct 108after successful validation. 109 110Ideally the server should close long-lived connections according to their auth 111expiry time. 112 113## JWT lower level APIs 114 115The related apis are in `./include/libwebsockets/lws-jws.h` 116 117### Validation of JWT 118 119``` 120int 121lws_jwt_signed_validate(struct lws_context *ctx, struct lws_jwk *jwk, 122 const char *alg_list, const char *com, size_t len, 123 char *temp, int tl, char *out, size_t *out_len); 124``` 125 126### Composing and signing JWT 127 128``` 129int 130lws_jwt_sign_compact(struct lws_context *ctx, struct lws_jwk *jwk, 131 const char *alg, char *out, size_t *out_len, char *temp, 132 int tl, const char *format, ...); 133``` 134 135## JWT creation and cookie get / set API 136 137Both the validation and signing apis use the same struct to contain their 138aguments. 139 140``` 141struct lws_jwt_sign_set_cookie { 142 struct lws_jwk *jwk; 143 /**< entry: required signing key */ 144 const char *alg; 145 /**< entry: required signing alg, eg, "ES512" */ 146 const char *iss; 147 /**< entry: issuer name to use */ 148 const char *aud; 149 /**< entry: audience */ 150 const char *cookie_name; 151 /**< entry: the name of the cookie */ 152 char sub[33]; 153 /**< sign-entry, validate-exit: subject */ 154 const char *extra_json; 155 /**< sign-entry, validate-exit: 156 * optional "ext" JSON object contents for the JWT */ 157 size_t extra_json_len; 158 /**< validate-exit: 159 * length of optional "ext" JSON object contents for the JWT */ 160 const char *csrf_in; 161 /**< validate-entry: 162 * NULL, or an external CSRF token to check against what is in the JWT */ 163 unsigned long expiry_unix_time; 164 /**< sign-entry: seconds the JWT and cookie may live, 165 * validate-exit: expiry unix time */ 166}; 167 168int 169lws_jwt_sign_token_set_http_cookie(struct lws *wsi, 170 const struct lws_jwt_sign_set_cookie *i, 171 uint8_t **p, uint8_t *end); 172int 173lws_jwt_get_http_cookie_validate_jwt(struct lws *wsi, 174 struct lws_jwt_sign_set_cookie *i, 175 char *out, size_t *out_len); 176``` 177