diff --git a/astro/astro.config.mjs b/astro/astro.config.mjs index f3c5121d..f5f6a3bc 100644 --- a/astro/astro.config.mjs +++ b/astro/astro.config.mjs @@ -277,6 +277,11 @@ export default defineConfig({ collapsed: true, items: [{ autogenerate: { directory: "introspection" } }], }, + { + label: "Learning Identity", + collapsed: true, + items: [{ autogenerate: { directory: "learning-identity" } }], + }, ], }), redirectFrom({ diff --git a/astro/src/content/docs/learning-identity/index.mdx b/astro/src/content/docs/learning-identity/index.mdx new file mode 100644 index 00000000..d55386a1 --- /dev/null +++ b/astro/src/content/docs/learning-identity/index.mdx @@ -0,0 +1,21 @@ +--- +title: Learning Identity +description: Overview of Identity concepts that can be learned in this area of the site. +sidebar: + label: Learning Identity + order: 1 +--- + +import { CardGrid, LinkCard } from "@astrojs/starlight/components"; + +The concept of Identity spans many topics. This section of documentation is meant to teach the basics of those individual concepts. + +## Key Concepts + + + + diff --git a/astro/src/content/docs/learning-identity/jwk.mdx b/astro/src/content/docs/learning-identity/jwk.mdx new file mode 100644 index 00000000..fe6690d3 --- /dev/null +++ b/astro/src/content/docs/learning-identity/jwk.mdx @@ -0,0 +1,27 @@ +--- +title: JWKs +description: Overview of what a JWK is, how it can used, and how it differs from a JWT. +sidebar: + label: JWKs + order: 101 +--- + +import { CardGrid, LinkCard } from "@astrojs/starlight/components"; + +# JWTs + +JSON Web Tokens (JWTs) are + +They are defined in the IETF's [RFC7519](https://datatracker.ietf.org/doc/html/rfc7519) as an open standard. + + +## Reading JWTs + +JWTs are encoded, and thus are not human readable by default. They must be decoded. + +> Note: Encoded does not mean encrypted. Anyone can decode a JWT. + + + + + diff --git a/astro/src/content/docs/learning-identity/what-is-a-jwt.mdx b/astro/src/content/docs/learning-identity/what-is-a-jwt.mdx new file mode 100644 index 00000000..e56937df --- /dev/null +++ b/astro/src/content/docs/learning-identity/what-is-a-jwt.mdx @@ -0,0 +1,116 @@ +--- +title: What is a JWT +description: Overview of what a JWT is and it's structure. +sidebar: + label: What is a JWT + order: 100 +--- + +JSON Web Tokens (JWTs) are JSON formatted strings which contain information about a subject, a human user or some other system like an API service, or AI Agent. The JWT is generated after the subject authenticates with the system by entering their username/password, providing a passkey, or authenticating any other way. + +The official standard for JWTs is the IETF's [RFC7519](https://datatracker.ietf.org/doc/html/rfc7519), and we will be referencing it below. + +The structure of a JWT follows the JSON Web Signature (JWS) format defined in [RFC7515](https://datatracker.ietf.org/doc/html/rfc7515). This means they are formatted into three sections separated by a dot. Each section is defined below, but they are all self-contained, Base64 URL Encoded strings. + +> Reminder: Encoded does not mean encrypted. Anyone can decode a JWT and read the plaintext values. The JWT is Base64 URL Encoded to make it simple to transmit the string between services by avoiding characters that can be problematic on the web, like spaces and quotes. + +## Reading JWTs + +All programming ecosystems have libraries to parse a JWT, so you don't need to implement the parsing logic yourself, regardless of your technology stack. For the curious, if you want to make your JWT instance human-readable, you can use the [Duende JWT Decoder](https://demo.duendesoftware.com/jwt-decoder). + +## Sections of a JWT + +The three sections of the JWT are Base64 URL Encoded strings separated by a dot. They are the Header, Payload, and Signature. + +An example JWT value `eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk` would be decoded as: +- Header: `{"typ": "JWT", "alg": "HS256"}` +- Payload: `{ "iss": "joe", "exp": 1300819380, "http://example.com/is_root": true }` +- Signature: `dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk` + +### Header + +The header, also called the JSON Object Signing and Encryption Header (JOSE Header), contains properties describing the cryptographic operations performed to sign the JWT payload. The example below shows that the `HS256` algorithm was used to sign the payload. + +```json +{ + "alg": "HS256", + "typ": "JWT" +} +``` + +### Payload + +The payload section of the JWT contains the Claims for the subject, which is information about the identity. This includes Registered Claim Names, a set of claims with known meanings, and/or any number of custom claims that are specific to your application(s). + +#### Registered Claim Names + +Some examples of Registered Claim Names are: +- `sub`: The subject the JWT is for. This can be any string that uniquely identifies the subject, like an email address or GUID. +- `iss`: The issuer of the JWT. This is typically the URI of the system that generated the JWT. +- `iat`: The date/time the JWT was issued. The value is the number of seconds since January 1, 1970. +- `exp`: The date/time the JWT expires. Systems receiving a JWT should not trust one after its expiration time has passed. The value is the number of seconds since January 1, 1970. + +The full set of Registered Claim Names are listed in the [JWT RFC](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1). + +#### Custom Claims + +Custom Claims are any claims that are specific to your application(s). For example, you can define the `department` claim to be a string that specifies which department the user works for. The actual value is stored in a database and loaded when the user signs in. + +> Note: An identity system can add any number of claims. Some systems will add everything it knows about the subject, others will scope them only to the system that will use the JWT. + +#### Real World Example + +The below example shows a payload with a mix of claims. The `sub` and `exp` are registered claims, and the rest are custom claims used by the consuming application. + +```json +{ + "sub": "user@example.com", + "exp": 1300819380, + "client_id": "my-frontend", + "department": "I.T.", + "team_name": "core developers", + "scope": [ + "admin", + "orders:read", + "inventory:read", + "inventory:write" + ] +} +``` + +> Note: A JWT may contain every claim for a user, making the transmitted string very large. Conversely it can contain a minimum amount of information about the subject. The amount of information contained in the JWT is determined by the service generating it. + +### Signature + +The signature is used to verify the JWT has not been tampered with since it was created. Because the token is an arbitrary string, the Signature adds trust that the JWT is the same value that it was when it was generated by the identity system (the trusted source of truth). Without verifying the signature, anyone could modify a JWT and get full access to the system. + +## Pseudocode Generating a JWT + +If it helps to visualize the process of generating a JWT with code, the following pseudocode describes what steps are taken to generate one: + +``` +//Create the Header and Payload JSON strings +var header = "{ "alg": "HS256", "typ": "JWT" }"; +var payload = "{ "sub" : "user@email.com" }"; + +//Base64 URL Encode the Header and Payload values +var headerBase64 = Base64Encode(header); +var payloadBase64 = Base64Encode(payload); + +//Generate the signature +// Create the signature, +// sign it with the algorithm mentioned in the header and using a secret, +// then Base64 URL Encode it +var signature = $"{headerBase64}.{payloadBase64}"; +var signingSecret = LoadSigningSecretFromSecureLocation(); +var signedSignature = SignWithHmacSha256(signature, signingSecret); +var signedSignatureBase64 = Base64Encode(signedSignature); + +var jwt = $"{headerBase64}.{payloadBase64}.{signedSignatureBase64}"; +print(jwt); +``` + +With the output looking something like: +```text +eyJhbGciOiJSUzI1NiIsImtpZCI6IkNEMzFENUE3NzVBQkI4MTU0REFGODRCMEEwMzM5MUMxIiwieDV0IjoiZGJLb1RyTDV6M0U4elR3UmdFbHlYc0tFbTcwIiwidHlwIjoiYXQrand0In0.eyJpc3MiOiJodHRwczovL2RlbW8uZHVlbmRlc29mdHdhcmUuY29tIiwibmJmIjoxNzg0MjMzMDU4LCJpYXQiOjE3ODQyMzMwNTgsImV4cCI6MTc4NDIzNjY1OCwiYXVkIjoiYXBpIiwic2NvcGUiOlsiYXBpIl0sImNsaWVudF9pZCI6Im0ybSIsImp0aSI6IkVDMjgwOTkwMkQ3NjIxRjYzNTBCMTU0NDBCNjFGRENBIn0.Q9ULRZ7sIN2HQ_7mzdS2JHqNQeD8duor6z6aUZIO3JwKK1iuRamahCiOXnQLxX7CJd0hnnP6b1K5ivXpAWhuwVSJcAbobyyKs1ihzcTxCvQPurEYDzQYWm_oIMgL5LI4uIUop7k9N2odHTZakustt97yVFDHWV0Zm9MMrTbf6kj-LnohPZlvv7YuByj9KlPZbD88n58hxYeK3_u3xdPpuHIoJWQT-RKzH_hd-WAN4ZkI5Tx4x8riuR9jsQRu2SsdSjGcR-cTwUHimlSaoaz8bhx5v-gr-8WGAWn7JVwA8ieBeLDiHyBmJz52W1qipO67aif7Sb-ky2TjnwvqYglgMg +```