Issuing Tokens
Generate signed tokens on login.
Issuing Tokens is a free C# Academy lesson on CoddyKit. This is lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the C# Academy learning path, and your progress syncs across the web and the CoddyKit app. The C# Academy course includes 4 lessons in total.
Issuing Versus Validating
So far you validated tokens. Now you will issue them: after a user logs in successfully, your API mints a signed JWT and returns it.
The core type for this is JwtSecurityTokenHandler from System.IdentityModel.Tokens.Jwt.
dotnet add package System.IdentityModel.Tokens.JwtThe Signing Key
To sign a token you need a key. For HS256 you reuse the same symmetric secret the validator uses.
Keep this secret out of source control - load it from configuration or a secret store.
var key = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(configuration["Jwt:Key"]!));SigningCredentials
SigningCredentials pairs the key with the algorithm. For a symmetric key, use SecurityAlgorithms.HmacSha256.
var creds = new SigningCredentials(
key,
SecurityAlgorithms.HmacSha256);Building the Claims
Decide what goes into the token. Always include a subject (the user id) and any roles or data the API needs.
Add a unique jti so each token can be identified.
var claims = new List<Claim>
{
new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString()),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.Role, user.Role)
};Creating the JwtSecurityToken
JwtSecurityToken bundles the issuer, audience, claims, expiry and signing credentials into a token object.
var token = new JwtSecurityToken(
issuer: configuration["Jwt:Issuer"],
audience: configuration["Jwt:Audience"],
claims: claims,
notBefore: DateTime.UtcNow,
expires: DateTime.UtcNow.AddMinutes(15),
signingCredentials: creds);Serializing to a String
The token object must be written to its compact header.payload.signature string form before sending it to the client.
var handler = new JwtSecurityTokenHandler();
string jwt = handler.WriteToken(token);
// jwt now looks like eyJhbGciOiJI...A Reusable Token Service
Wrap the logic in a service so controllers stay clean and the configuration lives in one place.
public class TokenService(IConfiguration config)
{
public string CreateAccessToken(AppUser user)
{
var key = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(config["Jwt:Key"]!));
var creds = new SigningCredentials(
key, SecurityAlgorithms.HmacSha256);
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString()),
new Claim(ClaimTypes.Role, user.Role)
};
var token = new JwtSecurityToken(
config["Jwt:Issuer"], config["Jwt:Audience"],
claims, expires: DateTime.UtcNow.AddMinutes(15),
signingCredentials: creds);
return new JwtSecurityTokenHandler().WriteToken(token);
}
}Wiring the Login Endpoint
The login endpoint verifies credentials, then calls the service and returns the token to the client.
app.MapPost("/login", (LoginDto dto, TokenService tokens) =>
{
var user = Authenticate(dto.Username, dto.Password);
if (user is null) return Results.Unauthorized();
return Results.Ok(new { accessToken = tokens.CreateAccessToken(user) });
});Symmetric Versus Asymmetric
HS256 uses one shared secret for both signing and validating. RS256 uses a private key to sign and a public key to validate.
Asymmetric signing lets you publish the public key so other services validate tokens without ever holding the signing secret.
var creds = new SigningCredentials(
new RsaSecurityKey(rsaPrivateKey),
SecurityAlgorithms.RsaSha256);Registering the Service
Add the token service to dependency injection so it can be injected into endpoints and controllers.
builder.Services.AddScoped<TokenService>();Keep Tokens Lean and Short
Two best practices when issuing tokens:
- Lean: include only claims the API needs - the token travels on every request.
- Short-lived: 5 to 15 minute access tokens limit the damage if one leaks.
expires: DateTime.UtcNow.AddMinutes(15)Quick Check
Confirm what you learned about issuing tokens.
Recap
You issued signed JWTs:
SigningCredentialspairs your key with an algorithm.- Build a list of
Claims, then aJwtSecurityTokenwith issuer, audience and expiry. JwtSecurityTokenHandler.WriteTokenserializes it to the compact string.- Keep tokens lean and short-lived.
Next: refresh tokens and expiry strategies.
Learn C# with an AI tutor — free
Write and run real code in your browser, get instant help from a 24/7 AI tutor, and pick up where you left off on the web or in the app.
- Courses
- 93
- Lessons
- 346
Frequently Asked Questions
Is the “Issuing Tokens” lesson free?
Yes — the full text of “Issuing Tokens” is free to read here on the web. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the C# Academy course, upgrade to CoddyKit PRO. The C# Academy course includes 4 lessons in total.
What will I learn in “Issuing Tokens”?
Generate signed tokens on login. You practise C# Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start C# Academy?
No prior experience is required. C# Academy on CoddyKit is structured for beginners through advanced learners, so you can start here or from the beginning and move at your own pace. This is lesson 3 of 4.
How long does the “Issuing Tokens” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this C# Academy lesson?
Yes. Every C# Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- JWT Structure and Claims
- Configuring JWT Bearer Authentication
- Issuing Tokens
- Refresh Tokens and Expiry