Tutorials / Build a REST API with C# and .NET · Lesson 35 of 38

Concept

On this page

    What a JWT Is

    The shop has two kinds of people who use the screens. Staff add, edit and delete books. The manager does that too, and also reads the stock report, which shows what the stock is worth. Today anyone who knows the address can open both pages, and the owner wants the report kept for managers.

    That raises a puzzle from HTTP and REST concepts: the server does not remember you between requests. So after you log in, how does the API know it is still you on the next request, and the one after that?

    Our goal: understand how a logged-in user proves who they are on every request, with a small signed note called a JWT.

    Here is the route:

    1. The two questions every protected request answers.
    2. A story first: the visitor badge in an office building.
    3. The same story on the web: a token, sent with every request.
    4. A real token, taken apart into its three pieces.
    5. Signed is not the same as secret: what never goes in a token.
    6. Why a token at all, and the other common way to stay logged in.

    This is a read-along lesson. There is no code to write and nothing to run. The next three lessons build it: the login endpoint makes tokens, protect endpoints and roles checks them, and the login screen keeps one in the browser. Left out on purpose: logging in with a Google or Microsoft account, tokens that renew themselves, and encrypted tokens.


    Two questions

    Meet the two people we use for the rest of the course. alice is the manager. bob works on the shop floor, as staff. (The lower-case names are their user names.)

    Every time bob or alice asks the API for something that is not public, the API needs two answers, in this order:

    Question Its name Example
    Who are you? Authentication bob types his user name and password, and the API agrees that this is bob.
    What are you allowed to do? Authorization bob asks for the stock report. The API knows it is bob, and says no: the report is for managers.

    The two words look alike, and people mix them up all the time. Keep the order in mind: authentication comes first, because you cannot decide what someone may do before you know who they are.

    What bob may do comes from his role: a label such as Staff or Manager that groups people who may do the same things. The API does not list what bob may do; it lists what each role may do, and bob has one role.


    The visitor badge

    Before any web words, a story you have probably lived.

    You visit an office building. At reception, you show your identity card once. The receptionist prints a badge for you. It says your name, your role and the time it stops being valid, and it carries a stamp that only reception can make. After that, nobody asks for your identity card again. Each door in the building has a guard who reads your badge, checks the stamp and the time, and decides.

    Reception, the badge and two doors Reception checks your ID once VISITOR BADGE Name: bob Role: Staff Until: 18:00 stamp Stockroom any valid badge Accounts office Manager badges only

    Follow bob through four doors, one at a time:

    1. bob at the stockroom. The badge says Staff, the stamp is right, it is 10:00. The guard lets him in.
    2. bob at the accounts office. The stamp is right, so the guard knows this is bob. But the badge says Staff, and this door is for managers. The guard says: "I know who you are, and you may not come in."
    3. bob changes his badge. He crosses out Staff and writes Manager with a pen. The stamp was made for the badge as it was printed, so it no longer matches. The guard does not trust a single word on this badge, and turns him away.
    4. bob at 19:00. The badge has run out. Even the stockroom guard sends him back to reception for a new one.

    Three things make this work. The guards never phone reception: everything they need is on the badge. Anyone in the corridor can read the badge, but nobody except reception can make a stamp that matches. And the badge has an end time, so a lost badge stops working by itself.


    The same story on the web

    Every part of the story has a twin in our system:

    In the building In the bookshop
    Reception The API's login endpoint, which checks a user name and password
    Identity card The user name and password
    Badge A token: a short piece of text the API hands back after login
    The stamp The token's signature (explained below)
    A door guard The API, checking the token on every request
    The end time The token's expiry time
    A door for managers only An endpoint such as GET /reports/stock

    The token our API uses is a JWT, short for JSON Web Token. People say it as "jot". It is a standard format, so every language and framework can make and check one.

    Here is bob's day as requests and responses. Time runs down the picture:

    Log in once, then show the token on every request bob's browser The API POST /auth/login (bob + password) 200, here is your token GET /books + token 200, the books GET /reports/stock + token refused: Staff may not

    bob sends his password once. After that, every request carries the token instead, in a header (one of the extra lines of a request, from the HTTP lesson). The header has a fixed name and form:

    Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQi...
    

    The name of the header is Authorization (a slightly confusing name, since it carries the answer to "who are you?"). Its value is the word Bearer, a space, and the token. Bearer means "whoever carries this": the API does not check who sends the token, only that the token is good. So a stolen token works for the thief until it runs out, exactly like a lost badge. Treat a token like a password.

    This answers the puzzle from the start. The server still remembers nothing between requests. It does not need to: the token travels with every request, and it holds everything the API needs to know about bob.


    A real token, taken apart

    This is a real token. Our API made it for bob when we ran the login endpoint of the next lesson, and the text is one long line:

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJCb29rU2hvcCIsImlzcyI6IkJvb2tTaG9wLkFwaSIsImV4cCI6MTc5MzQ0Mzk0MywibmFtZSI6ImJvYiIsInJvbGUiOiJTdGFmZiIsImlhdCI6MTc5MzQ0MDM0MywibmJmIjoxNzkzNDQwMzQzfQ._dlpMAmhQU7RATNTOXovXe4H_gXos0snljjUYR9pyuw
    

    It looks like noise, but look for the two dots. They cut it into three pieces:

    The three pieces of a JWT eyJhbGci... . eyJhdWQiOiJC... . _dlpMAmh... Header how it is signed Payload the facts about bob Signature the stamp

    The first two pieces are JSON, written in Base64url: a way to write any text using only letters, digits, - and _, so it travels safely inside a header. It is not a code or a secret. Anyone can turn it back into the JSON. Your browser can do it: in the browser's developer tools (the Console tab from the first web page), atob turns Base64 back into text. We gave it the first piece:

    atob('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9')
    
    '{"alg":"HS256","typ":"JWT"}'
    

    And then the second piece:

    atob('eyJhdWQiOiJCb29rU2hvcCIsImlzcyI6IkJvb2tTaG9wLkFwaSIsImV4cCI6MTc5MzQ0Mzk0MywibmFtZSI6ImJvYiIsInJvbGUiOiJTdGFmZiIsImlhdCI6MTc5MzQ0MDM0MywibmJmIjoxNzkzNDQwMzQzfQ')
    
    '{"aud":"BookShop","iss":"BookShop.Api","exp":1793443943,"name":"bob","role":"Staff","iat":1793440343,"nbf":1793440343}'
    

    (atob only understands plain Base64. It worked on these two pieces because they happen to contain no - or _. Websites such as jwt.io decode any token and show the pieces side by side. That is fine for a practice token like this one; never paste a real user's token into a website.)

    The header

    {"alg":"HS256","typ":"JWT"} says two things: this is a JWT, and the stamp was made with HS256, a common way of signing with one secret key. We come back to it under the signature.

    The payload

    The payload holds the facts. Each fact is called a claim: something the token claims about its holder, which the API believes because of the stamp. Here is each one in bob's token:

    Claim Value Meaning
    name bob Who this is. We chose this claim.
    role Staff What bob is. We chose this claim too.
    iss BookShop.Api The issuer: who made the token.
    aud BookShop The audience: who the token is for. A token made for another system is refused.
    iat 1793440343 Issued at: when it was made.
    nbf 1793440343 Not before: it is not valid before this moment.
    exp 1793443943 Expires: it is not valid after this moment.

    The short names (iss, aud, exp and the others) come from the JWT standard, so that every library reads them the same way. name and role are ours.

    The three times are counted in seconds since the start of 1 January 1970, a common way for computers to write a moment. They are in UTC, the world's reference time, so they mean the same moment in every country. 1793440343 is 31 October 2026 at 09:52:23 UTC, when we ran the login. The expiry is 3600 seconds later: 1793443943 − 1793440343 = 3600, which is one hour. After 10:52:23, the token is the badge at 19:00.

    The signature

    The third piece is the stamp. The API made it from three things: the header, the payload, and a secret key, a long password that only the API knows and that never leaves the server. HS256 mixes them into a short code. The same three inputs always give the same code. Change one letter of the payload, and the code comes out completely different.

    Making the stamp, then checking it At login: make the stamp header payload secret key HS256 signature On each request: check it header payload same key new signature Does it match the signature that came with the token? yes: trust the claims · no: refuse the whole token

    When a request arrives, the API does the work again: it takes the header and the payload that came with the request, mixes them with its own key, and compares the result with the signature that came along. Same code: nobody has touched the token since the API made it, so the API believes every claim. A different code: someone changed something, like bob with his pen, and the API refuses the whole token.

    bob cannot make a matching stamp for a payload that says Manager, because he does not have the key. That is why the key must stay secret, on the server, and must never be written into a web page.


    Signed is not secret

    The signature stops anyone from changing a token. It does not stop anyone from reading it. You decoded bob's token above without any key. A signed token is like the badge on bob's shirt: anyone in the corridor can read it.

    What may go in a token Fine in a token user name role expiry time Never in a token a password the shop's cost prices anything private

    So the rule: put in a token only what the holder may see, and what the API needs on every request. Encrypted tokens exist (they hide the payload too). We do not need one: our token carries only a name and a role.


    Why a token, and the other way

    The honest question: why hand out a token at all? The server could remember who has logged in.

    That is the other common way, and it is older. At login, the server makes a random number, called a session id, and keeps a list: "session 4821 is bob". It sends the number to the browser in a cookie: a small value the browser stores and sends back by itself with every request to the same site. On each request, the server looks the number up in its list.

    A session against a token A session browser 4821 server its list: 4821 = bob Staff A token browser bob, Staff + stamp server only the key

    Both ways work, and both are used everywhere. What differs:

    Session Token (JWT)
    Who knows that it is bob The server, in its list The token itself
    What the server keeps A list of everyone logged in Only its secret key
    Log someone out right now Easy: delete the line from the list Hard: the token stays good until it expires
    Who can use it Mostly browsers, which handle cookies by themselves Any program that can send a header

    A realistic future. Today our only client is the web pages. A shop like this soon gets more: a phone app for the shop floor, a nightly job that updates stock, a partner's system that reads the catalogue. Each of them can send an Authorization header with a token, and the API checks it the same way for all of them, with no list to share between servers. That is why tokens are a common choice for APIs.

    The cost, said honestly. A token cannot be taken back. If bob leaves the shop at 10:00, his token still works until 10:52. Short expiry times keep that window small; ours is one hour. Bigger systems add tokens that renew themselves, which this course leaves out.

    Not over-engineering. Our pages and our API share one address, and the only client is a browser. For exactly that case, a session cookie is a fine choice, and often a simpler one: ASP.NET supports both. We use a JWT because it is widely used by APIs, it works for every kind of client, and you can see everything it holds. That is a choice, not the only right answer.


    Recap

    • Authentication answers "who are you?". Authorization answers "what may you do?", and it comes second. What a user may do comes from their role.
    • A JWT is a token the API hands out at login. Like a visitor badge, it says who you are, what you are, and until when, and it carries a stamp.
    • The browser sends it with every request, in the Authorization: Bearer ... header. So the server can stay stateless: the token carries what it needs.
    • A token has three pieces, joined by dots: the header (how it is signed), the payload (the claims, such as name, role and exp) and the signature.
    • The signature is made from the header, the payload and a secret key that only the API has. Change one letter and the API refuses the whole token.
    • Signed is not secret. Anyone can read a token, so it never holds a password or private data. And whoever holds it can use it, so keep it safe.
    • A session cookie is the other common way. A token suits APIs with many kinds of clients, and it cannot be taken back before it expires, so keep its life short.

    Questions to think about

    Each question has its answer folded away underneath. Guess first, then open it to check.

    1. What is the difference between authentication and authorization?
    Show answer

    Authentication finds out who you are, for example from a user name and password. Authorization decides what you may do, for example whether you may see the report. Authorization needs the answer to authentication first.

    1. The server remembers nothing between requests. How does it know that a request comes from bob?
    Show answer

    Every request carries bob's token in the Authorization header. The token says who he is, and the signature proves the API made it.

    1. bob decodes his token, changes Staff to Manager and sends it. What happens, and why?
    Show answer

    The API refuses the token. The signature was made for the original payload with a key bob does not have, so it no longer matches the changed one.

    1. Why must a token never hold a password?
    Show answer

    A token is signed, not encrypted. Anyone who has it can decode the payload and read every claim.

    1. Where does a token come from in the first place?
    Show answer

    From a login endpoint: the user sends a user name and password once, and the API answers with a token. The next lesson builds it.

    Next lesson: the login endpoint, where the API checks a password and hands out a token.

    ← The Report ScreenThe Login Endpoint →