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