TILT–Authentication and Authorization–part 9

This is the difficult part. I decide that , for the moment, I do not need in the application any fancy authorization – just a simple url + secret ( a.k.a password).

I decided also to use JWT – it is a simple way to add authentication + authorization to the application.

Let’s see the login code

            var data = await search.TILT_URLSimpleSearch_URLPart(SearchCriteria.Equal, url).ToArrayAsync(); ;
            if (data == null)
                return null;

            if (data.Length != 1)
                return null;

            var item = data[0];
            if (item.Secret != secret)
                return null;

            var tokenHandler = new JwtSecurityTokenHandler();
            var key = Encoding.ASCII.GetBytes(SecretKey);
            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = new ClaimsIdentity(new Claim[]
                    {
                    new Claim(TokenId ,item.ID.ToString() ),
                    new Claim(ClaimTypes.Role, "Editor")
                    }),

                Expires = DateTime.UtcNow.AddMinutes(5), 
                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
            };

            var token = tokenHandler.CreateToken(tokenDescriptor);
            var ret = tokenHandler.WriteToken(token);
            return ret;

Nothing fancy – just SecretKey must be set in the appsettings.json file – and be enough large – otherwise an error occurs

For adding the TILTs to the own url , a custom authentication should be made. The code is not so simple, so I show here:

builder.Services.AddAuthorization(options=>

    options.AddPolicy("CustomBearer", policy =>
    {
        policy.AuthenticationSchemes.Add("CustomBearer");
        policy.RequireAuthenticatedUser();
    }));
builder.Services.AddAuthentication()
           .AddJwtBearer("CustomBearer",options =>
           {
              
               options.RequireHttpsMetadata = false;
               options.SaveToken = true;
               options.TokenValidationParameters = new TokenValidationParameters
               {
                   ValidateIssuerSigningKey = true,
                   IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(key)),
                   ValidateIssuer = false,
                   ValidateAudience = false
               };
               options.Events = new JwtBearerEvents()
               {
                   OnMessageReceived = ctx =>
                   {
                       if (!(ctx?.Request?.Headers?.ContainsKey("Authorization") ?? true))
                       {
                           ctx.NoResult();
                           return Task.CompletedTask;
                       };
                       var auth = ctx.Request.Headers["Authorization"].ToString();
                       if (string.IsNullOrEmpty(auth))
                       {
                           ctx.NoResult();
                           return Task.CompletedTask;
                       }
                       if (!auth.StartsWith("CustomBearer ", StringComparison.OrdinalIgnoreCase))
                       {
                           ctx.NoResult();
                           return Task.CompletedTask;
                       }

                       ctx.Token = auth.Substring("CustomBearer ".Length).Trim();
                       return Task.CompletedTask;

                   }
               };
           });

and the controller will be

    [Authorize(Policy = "CustomBearer", Roles = "Editor")]
    [HttpPost]
    public async Task<ActionResult<TILT_Note_Table>> AddTILT([FromServices] I_InsertDataApplicationDBContext insert, TILT_Note_Table note)
    {
        //TB: 2022-04-30 to be moved into a class - skinny controllers
        var c = this.User?.Claims.ToArray();
        var idUrl = auth.MainUrlId(c);
        if (idUrl == null)
        {
            return new UnauthorizedResult();
        }
        note.IDURL = idUrl.Value;
        note.ID = 0;
        note.ForDate = DateTime.UtcNow;
        var noteOrig = new TILT_Note();
        noteOrig.CopyFrom(note);
        await insert.InsertTILT_Note(noteOrig);
        note.CopyFrom(noteOrig);
        return note;

    }

The

        //TB: 2022-04-30 to be moved into a class - skinny controllers

is a time bomb comment- it will generate an error when compiling on the date.

Also, I have added CORS

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: "AllowAll",
                      policy =>
                      {
                          policy.AllowAnyHeader().AllowAnyMethod().AllowCredentials().SetIsOriginAllowed(it => true);
                      });
});
//code
var app = builder.Build();
//code
app.UseCors("AllowAll");

in order for the front-end ,no matter where it is , to work.

I have put SetIsOriginAllowed(it => true) in the policy – works with credentials.

Tools Used

RSCG_TimeBombComment

QueryGenerator Visual Studio

BoilerplateFree – to generate interface from an existing class

System.IdentityModel.Tokens.Jwt – to generate the token and decrypt the token Visual Studio