I have some problem with JWT token. I have 2 apllication. 1st - Blazor webAssemble which has authentification in server side. And 2nd - web api. In second app i want use token from 1st app. In same server it's working. I can get token from wep api Blazor and use this token for [Authorize] method in 2nd web api. But if i deploy 2nd app in other server, i will have http code 401. All token will invalid in this scenario.
In asp net 4.5 identity used machineKey for decrypt token. And machineKey must be synchronize between servers for use one token. But i can't find how it's wotk in .net 6.
Anyone know something about this? Perhaps, some post about it.
This is programm.cs from 2nd app. App without outhorization.
var builder = WebApplication.CreateBuilder(args);
var configuration = builder.Configuration;
// Add services to the container.
builder.Services.AddControllers();
TokenSettings tokenSettings = new TokenSettings();
configuration.Bind("Settings", tokenSettings);
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(o =>
{
o.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
{
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(tokenSettings.Key)),
ValidIssuer = tokenSettings.Issuer,
ValidAudience = tokenSettings.Audience,
ValidateIssuerSigningKey = true,
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true
};
});
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(opt =>
{
opt.SwaggerDoc("v1", new OpenApiInfo { Title = "MyAPI", Version = "v1" });
opt.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
In = ParameterLocation.Header,
Description = "Please enter token",
Name = "Authorization",
Type = SecuritySchemeType.Http,
BearerFormat = "JWT",
Scheme = "bearer"
});
opt.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type=ReferenceType.SecurityScheme,
Id="Bearer"
}
},
new string[]{}
}
});
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
Thanks.
UPDATE
It was app.UseHttpsRedirection(); After publish i was using http and api did redirect. But i was receiving 401 from this code. Idk why. MessageBox.Show(response.StatusCode.ToString());
Related
I am trying to authenticate an Appwrite user with my ASP.NET Core 7 Web API. In the past, I used Firebase for this with which I was able to implement the function as following:
private static void ConfigureFirebaseAuthentication(IServiceCollection services,
IConfiguration configuration)
{
var options = new AppOptions() { Credential = GoogleCredential.FromFile("firebase-config.json") };
FirebaseApp.Create(options);
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(
opt =>
{
opt.IncludeErrorDetails = true;
opt.Authority = configuration["FirebaseAuthentication:ValidIssuer"];
opt.TokenValidationParameters = new()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = configuration["FirebaseAuthentication:ValidIssuer"],
ValidAudience = configuration["FirebaseAuthentication:ValidAudience"]
};
}
);
}
This validated the request against the firebase API, but I don't see how I am able to implement something similar for Appwrite. Also the docs don't mention anything helpful.
Does anyone know how to achieve this?
Unfortunately, Appwrite doesn't have a .NET SDK yet so you would have to manually make the API call. I don't know .NET very well, but I generated code using the API specs and Insomnia:
var client = new HttpClient();
var request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri("https://[HOSTNAME]/v1/account/sessions/email"),
Headers =
{
{ "X-Appwrite-Project", "[PROJECT ID]" },
},
Content = new StringContent("{\n \"email\": \"[EMAIL]\",\n \"password\": \"[PASSWORD]\"\n}")
{
Headers =
{
ContentType = new MediaTypeHeaderValue("application/json")
}
}
};
using (var response = await client.SendAsync(request))
{
response.EnsureSuccessStatusCode();
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body);
}
If this is successful, you can grab the X-Fallback-Cookies response header and use that for future requests.
Otherwise, if you don't want to create a session server side and you have an Appwrite JWT token generated from your front end, you can make API calls to Appwrite and pass the JWT token in the X-Appwrite-JWT header to make requests on behalf of the user.
For more information on working directly with the Appwrite REST API, refer to the REST docs.
I created a asp.net core empty project running on .net6. I am coming across an issue when I am trying to enable authentication in swagger. Swagger UI runs as expected, the only issue is whenever I click on the Authorize green button on the swagger UI it will pop up but say Unknown Security definition type and give me two options Authorize and Close. It does not show under Available authorizations Bearer(http,Bearer) and allow me to enter a jwt value. I ran into this article that goes over bearer authentication but it isn't much help for me. Am I missing something in the .AddSwaggerGen()?
builder.Services.AddAuthorization();
builder.Services.AddSwaggerGen(options =>
{
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Scheme = "Bearer",
BearerFormat = "JWT",
In = ParameterLocation.Header,
Name = "Authorization",
Description = "Bearer Authentication with JWT Token",
Type = SecuritySchemeType.Http
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Id = "Bearer",
Type = ReferenceType.SecurityScheme
}
},
new List<string>()
}
});
});
var app = builder.Build();
app.UseAuthorization();
app.UseAuthentication();
......
I think Scheme should be "bearer" (lowercase b)
Scheme = "bearer",
You have used add authorization before defining your security definition.
First define security definition & then use add authorization
I'm trying to connect to a SignalR service from my blazor webassembly client but this fails I think on CORS. This is the code in my razor file.
m_connection = new HubConnectionBuilder()
.WithUrl(myMircoServiceUrl, options =>
{
options.AccessTokenProvider = () => Task.FromResult(userService.Token);
})
.WithAutomaticReconnect()
.Build();
await m_connection.StartAsync();
Then in the webassembly logging I see the following error:
Access to fetch at 'xxxx/negotiate?negotiateVersion=1' from origin 'http://localhost:5010' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
I added the following CORS policy in my Blazor server configuration and something similar in the microservice config:
app.UseResponseCompression();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBlazorDebugging();
}
else
{
app.UseExceptionHandler(#"/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseAuthorization();
app.UseCors(policy => policy
.WithOrigins("http://localhost:5010")
.AllowAnyHeader()
.AllowAnyMethod());
app.UseClientSideBlazorFiles<Client.Program>();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapFallbackToClientSideBlazor<Client.Program>(#"index.html");
});
Anybody got any idea what might be wrong?
Update 1
I now see the following error in the Chrome console:
dotnet.js:1 WebSocket connection to 'ws://localhost:5000/hubs/posts?id=9Jxs0DhP924zgw_eIeE9Lg' failed: HTTP Authentication failed; no valid credentials available
Update 2
I removed the [Authorize] attribute from the SignalR hub and now it connects. And I can send messages to the hub. Problem is there is a reason for this attribute, because I don't want that people can subscribe to messages that are not for them
Update 3
Still no progress. Looking at pulling out the authentication to a seperate microservice using IdentityServer4. Last status is I have the following startup routines:
Microservice: gist.github.com/njannink/15595b77ffe1c0593be1a555fa37f83f
Blazor server: gist.github.com/njannink/7302a888110e24d199ea45b66da4f26b
Blazor client: gist.github.com/njannink/add2568cbf48c8b3c070ccd4f28fd127
I've got the same errors with CORS and afterwards Websocket.
In my case the fallback longPolling was used as why the connection worked but the console logged the error HTTP Authentication failed; no valid credentials available.
If you use Identity Server JWT the following code solved the error for my case.
(The Code is from the Microsoft SignalR Documentation - Authentication and authorization in ASP.NET Core SignalR - Identity Server JWT authentication)
services.AddAuthentication()
.AddIdentityServerJwt();
// insert:
services.TryAddEnumerable(
ServiceDescriptor.Singleton<IPostConfigureOptions<JwtBearerOptions>,
ConfigureJwtBearerOptions>());
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.Options;
public class ConfigureJwtBearerOptions : IPostConfigureOptions<JwtBearerOptions>
{
public void PostConfigure(string name, JwtBearerOptions options)
{
var originalOnMessageReceived = options.Events.OnMessageReceived;
options.Events.OnMessageReceived = async context =>
{
await originalOnMessageReceived(context);
if (string.IsNullOrEmpty(context.Token))
{
var accessToken = context.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) &&
path.StartsWithSegments("/hubs"))
{
context.Token = accessToken;
}
}
};
}
}
Important: Your Route has to start with hubs for the Options to trigger!
(see Line path.StartsWithSegments("/hubs")))
app.UseEndpoints(e =>
{
...
e.MapHub<ChatHub>("hubs/chat");
});
In my case, ASP.NET Core 2.2 I have an API from which I want to be able to use SignalR from the API to connect to my client application.
I have Projects for
Web API
IdentityServer4
MVC Client
With ASP.NET Core Identity as the for user management
In order for your user to be authenticated you need to implement a IUserIdProvider like this
public class IdBasedUserIdProvider : IUserIdProvider
{
public string GetUserId(HubConnectionContext connection)
{
//TODO: Implement USERID Mapper Here
//throw new NotImplementedException();
//return whatever you want to map/identify the user by here. Either ID/Email
return connection.User.FindFirst("sub").Value;
}
}
With this I make sure I am pushing along the ID/Email to a method I am calling either from the Server or Client. Although I can always use the .User on the HubContext and it works fine.
In my Web API Startup.cs file I came up with
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(cfg =>
{
cfg.AddDefaultPolicy(policy =>
{
policy.WithOrigins(Configuration.GetSection("AuthServer:DomainBaseUrl").Get<string[]>())
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials()
.SetIsOriginAllowed((_) => true)
.SetIsOriginAllowedToAllowWildcardSubdomains();
});
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, UserManager<AppUser> userManager,
RoleManager<IdentityRole> roleManager){
app.UseCors();
}
NOTE
Configuration.GetSection("AuthServer:DomainBaseUrl").Get() retrieves the list of domains to allow CORS for from a config file.
And I did this configuration in My Client App COnfigureService Method
services.AddCors(cfg =>
{
cfg.AddDefaultPolicy(policy => {
policy.AllowAnyHeader();
policy.AllowAnyMethod();
policy.SetIsOriginAllowed((host) => true);
policy.AllowAnyOrigin();
});
});
I hope this helps your situation.
The best solution is indeed as Ismail Umer described using a seperate authentication service using something like IdentityServer4. And use this service in all other services. This is something I will do in a next iteration.
As short term solution I temporary moved the blazor server part into my api service and use a dual authentication method (JWT header or cookie).
var key = Encoding.UTF8.GetBytes(m_configuration[#"SecurityKey"]);
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = #"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier",
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true
};
})
.AddCookie();
// TODO: For time being support dual authorization. At later stage split in various micro-services and use IdentityServer4 for Auth
services.AddAuthorization(options =>
{
var defaultAuthorizationPolicyBuilder = new AuthorizationPolicyBuilder(
CookieAuthenticationDefaults.AuthenticationScheme,
JwtBearerDefaults.AuthenticationScheme);
defaultAuthorizationPolicyBuilder =
defaultAuthorizationPolicyBuilder.RequireAuthenticatedUser();
options.DefaultPolicy = defaultAuthorizationPolicyBuilder.Build();
});
This is problem with Microsoft.AspNetCore.SignalR.Client 3.1.3.
You can read about it here in comments.
You can wait for update or temporarly fix this issue:
Disable negotiation
Set WebSocket transport explicitly
Modify query url
Add OnMessageReceived handler
Client side:
var token = await GetAccessToken();
var hubConnection = new HubConnectionBuilder()
.WithUrl($"/notification?access_token={token}", options =>
{
options.SkipNegotiation = true;
options.Transports = HttpTransportType.WebSockets;
options.AccessTokenProvider = GetAccessToken;
})
.Build();
Server side:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(options =>
{
// ...
})
.AddJwtBearer(options =>
{
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) &&
(path.StartsWithSegments("/notification", System.StringComparison.InvariantCulture)))
{
context.Token = accessToken;
}
return Task.CompletedTask;
},
};
});
}
I followed this tutorial to configure JWT authorization in my Web API app. The token generation and handout works fine, but when I send a request back to the server with the token, it doesn't populate the identity, so it fails if authorization is required.
I've tested both with a reactjs frontend and Postman. Both end up returning nothing (without Authorize decorator - User.Identity.isAuthorized is false), or 404 with the decorator. I have confirmed that the token is being sent properly.
I'm also using Identity, if that matters.
ConfigureServices method
public void ConfigureServices(IServiceCollection services)
{
...
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Jwt:Issuer"],
ValidAudience = Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
};
});
}
Configure method
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider serviceProvider)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseAuthentication();
app.UseCors("SiteCorsPolicy");
app.UseMvc();
...
}
Function to build the token
private string BuildToken(AuthViewModel user)
{
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, user.Username),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken
(
_config["Jwt:Issuer"],
_config["Jwt:Audience"],
//claims,
expires: DateTime.Now.AddMinutes(30),
signingCredentials: creds
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
Excerpt from appsettings.json
"Jwt": {
"Key": "<secret stuff>",
"Issuer": "http://localhost:53530/",
"Audience": "http://localhost:8080/"
}
Test function I'm trying to call but is failing
[HttpGet("currentuser"), Authorize]
public async Task<ApplicationUser> GetCurrentUser()
{
var username = User.Identity.Name;
return await _context.ApplicationUsers.SingleOrDefaultAsync(u => u.UserName == username);
}
I figured it out. I had to add a new Authorization Policy.
services.AddAuthorization(auth =>
{
auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser().Build());
});
Then I decorated the controller with
[Authorize("Bearer"]
I've been messing with this for a couple days, trying different tutorials, so I know this was working at one point without the policy. Dunno why I needed it this time or why it wasn't part of the tutorial.
If someone figures out what I screwed up in the first place, I'm all ears.
I ran into the same issue (.net core 2.1) and was really happy to make it work using your answer #atfergs.
After fiddling with the whole setup I found out that no new Authorization Policy is required.
It is sufficient to decorate the controller with
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
considering the following setup
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{...}
Now
User?.Identity?.IsAuthenticated
is true :)
Cheers!
I started doing angular2 + asp.net core application, started implementing Auth0. I created client application and a user.
Here is client application setup, provided url for Api:
User login works fine:
Now I have an api with this controller:
[Route("api")]
public class PingController : Controller
{
[Authorize]
[HttpGet]
[Route("ping/secure")]
public string PingSecured()
{
return "All good. You only get this message if you are authenticated.";
}
}
And in startup.cs I tried implementing like this:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
var options = new JwtBearerOptions
{
Audience = "uUdThU122xYPugR8gLoNTr3HdJ6sWvQV",
Authority = "https://dntquitpls.eu.auth0.com/",
};
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
};
app.UseJwtBearerAuthentication(options);
app.UseCors(builder =>
builder.WithOrigins("http://localhost:61290/").AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod()
);
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapWebApiRoute("defaultApi",
"api/{controller}/{id?}");
});
}
And it does not work getting this:
Api part is done by Auth0 Api tutorial, for example if I create a Api and there is a test Bearer token it works with that in api, also i configure Startup.cs file by that Api, but unfortunately with my Bearer token from response does not work.
Please any ideas why it does not work and I am not getting authorized?
Found a solution, now it works, the problem was in Startup.cs file in options HS256 Encoding, which is used for UseJwtBearerAuthentication, solution:
var keyAsBytes = Encoding.ASCII.GetBytes("CLIENT_SECRET");
var options = new JwtBearerOptions
{
TokenValidationParameters =
{
ValidIssuer = "https://dntquitpls.eu.auth0.com/",
ValidAudience = "uUdThU122xYPugR8gLoNTr3HdJ6sWvQV",
IssuerSigningKey = new SymmetricSecurityKey(keyAsBytes)
}
};
app.UseJwtBearerAuthentication(options);
source:
http://www.jerriepelser.com/blog/using-roles-with-the-jwt-middleware/
if you want to work with RS256 encoding use this:
var certificationData = Configuration["auth0:certificate"];
var certificate = new X509Certificate2(Convert.FromBase64String(certificationData));
var options = new JwtBearerOptions()
{
Audience = Configuration["auth0:clientId"],
Authority = Configuration["auth0:authority"],
AutomaticChallenge = true,
AutomaticAuthenticate = true,
TokenValidationParameters = {
ValidIssuer = Configuration["auth0:authority"],
ValidAudience = Configuration["auth0:clientId"],
IssuerSigningKey = new X509SecurityKey(certificate)
}
};
app.UseJwtBearerAuthentication(options);