DEV Community

Cover image for .Net 6 Minimal Api Authentication (JWT) with Swagger and Open API
Mohamad Lawand
Mohamad Lawand

Posted on

.Net 6 Minimal Api Authentication (JWT) with Swagger and Open API

In this article we will see how we can add JWT Token authentication to our Minimal API and how we will utilise Swagger to test it out

You can watch the full video on Youtube

Starting Github Repo:
(https://github.com/mohamadlawand087/v52-minimalAPi)

Final Github Rep:
(https://github.com/mohamadlawand087/MinimalApi-JWT)

Once we have pull our application we need to install a nuget package

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
Enter fullscreen mode Exit fullscreen mode

Now that we have completed the initial setup we need to start building our Minimal API

We will be using Swagger to test our MinimalAPI so we will start by updating our Swagger Configuration

var securityScheme = new OpenApiSecurityScheme()
{
    Name = "Authorization",
    Type = SecuritySchemeType.ApiKey,
    Scheme = "Bearer",
    BearerFormat = "JWT",
    In = ParameterLocation.Header,
    Description = "JSON Web Token based security",
};

var securityReq = new OpenApiSecurityRequirement()
{
    {
        new OpenApiSecurityScheme
        {
            Reference = new OpenApiReference
            {
                Type = ReferenceType.SecurityScheme,
                Id = "Bearer"
            }
        },
        new string[] {}
    }
};

var contact = new OpenApiContact()
{
    Name = "Mohamad Lawand",
    Email = "hello@mohamadlawand.com",
    Url = new Uri("http://www.mohamadlawand.com")
};

var license = new OpenApiLicense()
{
    Name = "Free License",
    Url = new Uri("http://www.mohamadlawand.com")
};

var info = new OpenApiInfo()
{
    Version = "v1",
    Title = "Minimal API - JWT Authentication with Swagger demo",
    Description = "Implementing JWT Authentication in Minimal API",
    TermsOfService = new Uri("http://www.example.com"),
    Contact = contact,
    License = license
};

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(o =>
{
    o.SwaggerDoc("v1", info);
    o.AddSecurityDefinition("Bearer", securityScheme);
    o.AddSecurityRequirement(securityReq);
});
Enter fullscreen mode Exit fullscreen mode

After our builder we need to add the following to enable Swagger

app.UseSwagger();
app.UseSwaggerUI();
Enter fullscreen mode Exit fullscreen mode

Once our Swagger is configured, let us now add our JWT configuration.

The first item of our configuration is updating our app settings to the following

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "Jwt": {
    "Issuer": "mohamadlawand.com",
    "Audience": "mohamadlawand.com",
    "Key": "ijurkbdlhmklqacwqzdxmkkhvqowlyqa"
},
  "AllowedHosts": "*"
}
Enter fullscreen mode Exit fullscreen mode

Next we need to add our JWT config to our builder

// Add JWT configuration
builder.Services.AddAuthentication(o =>
{
    o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    o.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(o =>
{
    o.TokenValidationParameters = new TokenValidationParameters
    {
       ValidIssuer = builder.Configuration["Jwt:Issuer"],
        ValidAudience = builder.Configuration["Jwt:Audience"],
        IssuerSigningKey = new SymmetricSecurityKey
            (Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"])),
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = false,
        ValidateIssuerSigningKey = true
    };
});

builder.Services.AddAuthorization();
builder.Services.AddEndpointsApiExplorer();
Enter fullscreen mode Exit fullscreen mode

Now we need to enable authentication and authorisation to our app

app.UseAuthentication();
app.UseAuthorization();
Enter fullscreen mode Exit fullscreen mode

Since we are expecting a dto lets create our authentication dto

record UserDto (string UserName, string Password);
Enter fullscreen mode Exit fullscreen mode

Now we need to get our endpoint which will give us the ability to generate and utilise our JWT token

app.MapPost("/security/getToken", [AllowAnonymous] (UserDto user) =>
{

    if (user.UserName=="admin@mohamadlawand.com" && user.Password=="P@ssword")
    {
        var issuer = builder.Configuration["Jwt:Issuer"];
        var audience = builder.Configuration["Jwt:Audience"];
        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]));
        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

        // Now its ime to define the jwt token which will be responsible of creating our tokens
        var jwtTokenHandler = new JwtSecurityTokenHandler();

        // We get our secret from the appsettings
        var key = Encoding.ASCII.GetBytes(builder.Configuration["Jwt:Key"]);

        // we define our token descriptor
            // We need to utilise claims which are properties in our token which gives information about the token
            // which belong to the specific user who it belongs to
            // so it could contain their id, name, email the good part is that these information
            // are generated by our server and identity framework which is valid and trusted
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(new []
            {
                new Claim("Id", "1"),
                new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
                new Claim(JwtRegisteredClaimNames.Email, user.UserName),
                // the JTI is used for our refresh token which we will be convering in the next video
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
            }),
            // the life span of the token needs to be shorter and utilise refresh token to keep the user signedin
            // but since this is a demo app we can extend it to fit our current need
            Expires = DateTime.UtcNow.AddHours(6),
            Audience = audience,
            Issuer = issuer,
            // here we are adding the encryption alogorithim information which will be used to decrypt our token
            SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha512Signature)
        };

        var token = jwtTokenHandler.CreateToken(tokenDescriptor);

        var jwtToken = jwtTokenHandler.WriteToken(token);

        return Results.Ok(jwtToken);
    }
    else
    {
        return Results.Unauthorized();
    }
});
Enter fullscreen mode Exit fullscreen mode

The last part is to make our endpoint secure by adding the authorise attribute

app.MapGet("/items", [Authorize] async (ApiDbContext db) =>
{
    return await db.Items.ToListAsync();
});

app.MapPost("/items", [Authorize] async (ApiDbContext db, Item item) => {
    if( await db.Items.FirstOrDefaultAsync(x => x.Id == item.Id) != null)
    {
        return Results.BadRequest();
    }

    db.Items.Add(item);
    await db.SaveChangesAsync();
    return Results.Created( $"/Items/{item.Id}",item);
});

app.MapGet("/items/{id}", [Authorize] async (ApiDbContext db, int id) =>
{
    var item = await db.Items.FirstOrDefaultAsync(x => x.Id == id);

    return item == null ? Results.NotFound() : Results.Ok(item);
});

app.MapPut("/items/{id}", [Authorize] async (ApiDbContext db, int id, Item item) =>
{
    var existItem = await db.Items.FirstOrDefaultAsync(x => x.Id == id);
    if(existItem == null)
    {
        return Results.BadRequest();
    }

    existItem.Title = item.Title;
    existItem.IsCompleted = item.IsCompleted;

    await db.SaveChangesAsync();
    return Results.Ok(item);
});

app.MapDelete("/items/{id}", [Authorize] async (ApiDbContext db, int id) => 
{
    var existItem = await db.Items.FirstOrDefaultAsync(x => x.Id == id);
    if(existItem == null)
    {
        return Results.BadRequest();
    }

    db.Items.Remove(existItem);
    await db.SaveChangesAsync();
    return Results.NoContent();
});
Enter fullscreen mode Exit fullscreen mode

Please comment your questions and subscribe to my YouTube channel

Top comments (1)

Collapse
 
blackpaw profile image
Blackpaw

Hi, thanks for the tutorial, well written and easy to follow.

One thing - the github repos your reference no longer exist?