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
Leave a Reply