xref: /aosp_15_r20/external/libwebsockets/READMEs/README.jwt.md (revision 1c60b9aca93fdbc9b5f19b2d2194c91294b22281)
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