Not able to Start SignalR server in WPF - wpf

I want to start the SignalR server , when I am starting the WPF application. So that clients can communicate. I have below code in WPF.
protected override async void OnStartup(StartupEventArgs e)
{
AppHost = Host.CreateDefaultBuilder()
.ConfigureServices(ConfigureRequiredServices(e))
.ConfigureWebHostDefaults(webBuilder => webBuilder
.UseUrls("http://localhost:5059")
.ConfigureServices(services => services.AddSignalR())
.Configure(app => {
app.UseRouting();
app.UseEndpoints(endpoint => { endpoint.MapHub<MyHub>("/myhub"); });
}))
.Build();
await AppHost!.StartAsync();
base.OnStartup(e);
}
Now when I am trying to reach to signalR server from console app, I am not able to do that. Any idea what I am missing?
Client code:
HubConnection connection = new HubConnectionBuilder()
.WithUrl("http://localhost:5059/myhub")
.Build();
connection.StartAsync();
connection.SendAsync("SendMessege", "A Message from Client");
connection.On<String>("ReceiveMessege", x => Console.Write(x));
Console.ReadLine();

Found the issue. Basically, ConfigureWebHostDefaults should precede, ConfigureServices. So below code works and able to get the server call. No change in the client.
AppHost = Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(webBuilder => webBuilder
.UseUrls("http://localhost:5059")
.ConfigureServices(services => services.AddSignalR())
.Configure(app => {
app.UseRouting();
app.UseEndpoints(endpoint => { endpoint.MapHub<MyHub>("/myhub");});
}))
.ConfigureServices(ConfigureRequiredServices(e))
.Build();

Related

Add SignalR to Net6 Web Api and connect from Winforms application (.net fx4.8) - hub not found

I'm trying to add signalr to the webapi, I create the CucinaHub class:
public class CucinaHub : Hub
{
static ConcurrentDictionary<string, string> _users = new ConcurrentDictionary<string, string>();
#region Client Methods
public void SetUserName(string userName)
{
_users[Context.ConnectionId] = userName;
}
#endregion Client Methods
}
and configure SignalR:
services.AddSignalR();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<CucinaHub>("/cucinahub");
});
Then in Windows form application I use this code to connect with the hub:
_signalRConnection = new HubConnection($"{Properties.Settings.Default.URL}/api", true);
_hubProxy = _signalRConnection.CreateHubProxy("/cucinahub");
_hubProxy.On("GetValvole", async () => await GetValvole());
try
{
//Connect to the server
await _signalRConnection.Start();
}
catch (Exception ex)
{
Log.Error(ex.ToString());
}
I get always 404 response code:
Hosting environment: Development
Content root path: D:\SwDev\PigsutffHTML\Server\Common\Common.WebApiCore
Now listening on: http://localhost:5000
Application started. Press Ctrl+C to shut down.
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
Request starting HTTP/1.1 GET http://localhost:5000/api/signalr/negotiate?clientProtocol=2.1&connectionData=[%7B%22Name%22:%22/cucinahub%22%7D] - -
warn: Microsoft.AspNetCore.HttpsPolicy.HttpsRedirectionMiddleware[3]
Failed to determine the https port for redirect.
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished HTTP/1.1 GET http://localhost:5000/api/signalr/negotiate?clientProtocol=2.1&connectionData=[%7B%22Name%22:%22/cucinahub%22%7D] - - - 404 0 - 122.4090ms
Where is the error?
Thank you
The key is using the package Microsoft.AspNetCore.SignalR.Client on the WinForm project as client. It is available on .NETFramework. You can see from the picture here:
For testing purpose, I created one webapi project and another winform project. Winform project has been installed the package. Then you can follow the MS document to set up the hubs in webapi project.
Then start the webapi project first to see which port it's running on. Now you can follow the MS Document for setting Clients to set client connect to hub.
For testing purpose, I created two button functions to connect hub and send message to hub, here is the code of connect and send message function button:
private void btnconnect_Click(object sender, EventArgs e)
{
try
{
connection.StartAsync().Wait();
}
catch (Exception ex)
{
//...
}
}
private void btnsend_Click(object sender, EventArgs e)
{
try
{
connection.InvokeAsync("SendMessage",
"winformclient", "hello world").Wait();
}
catch (Exception ex)
{
//...
}
}
And here is the code for setting up client connecting(Remember using your own port here):
HubConnection connection;
public Form1()
{
InitializeComponent();
connection = new HubConnectionBuilder()
.WithUrl("https://localhost:7090/ChatHub")
.Build();
connection.Closed += async (error) =>
{
await Task.Delay(new Random().Next(0, 5) * 1000);
await connection.StartAsync();
};
}
Now start the WinForm Application, click the send button and you can get the result here:
You can see from the screenshot, the hub has already got the message from WinForm application.
Short answer - You cannot mix the .NET 4.x Framework and the .NET Core server/clients. Neither one can talk to the other. You have to upgrade your client. More info in this SO answer.

Using a blazor server with signalR as a relay server

The goal is to use a Blazor server as a relay server using signalR.
I have little to no experience with blazor servers before this.
The Idea would be to connect a Winform/Xamarin client to this server, target the recipient using a name/id from an existing database, and relay the necessary info.
Hub:
[Authorize]
public class ChatHub : Hub
{
public Task SendMessageAsync(string user, string message)
{
//Context.UserIdentifier
Debug.WriteLine(Context.UserIdentifier);
Debug.WriteLine(Context?.User?.Claims.FirstOrDefault());
return Clients.All.SendAsync("ReceiveMessage", user, message); ;
}
public Task DirectMessage(string user, string message)
{
return Clients.User(user).SendAsync("ReceiveMessage", user, message);
}
}
As per documentation I'm trying to set the Context.UserIdentifier, I do however struggle with the authentication part. My program.cs looks like this:
var builder = WebApplication.CreateBuilder(args);
var services = builder.Services;
services.AddTransient<IUserIdProvider, MyUserIdProvider>();
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
//var accessToken = context.Request.Query["access_token"];
var accessToken = context.Request.Headers["Authorization"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/chathub"))
{
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddSignalR();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAuthentication();
app.UseRouting();
app.UseAuthorization();
app.MapBlazorHub();
app.MapHub<ChatHub>("/chathub");
app.MapFallbackToPage("/_Host");
app.Run();
As for my Client (a winform test client) I tried something like this:
HubConnection chatHubConnection;
chatHubConnection = new HubConnectionBuilder()
.WithUrl("https://localhost:7109/chathub", options =>
{
options.AccessTokenProvider = () => Task.FromResult(token);
})
.WithAutomaticReconnect()
.Build();
private async void HubConBtn_Click(object sender, EventArgs e)
{
chatHubConnection.On<string, string>("ReceiveMessage", (user, message) =>
{
this.Invoke(() =>
{
var newMessage = $"{user}: {message}";
MessagesLB.Items.Add(newMessage);
});
});
try
{
await chatHubConnection.StartAsync();
MessagesLB.Items.Add("Connected!");
HubConBtn.Enabled = false;
SendMessageBtn.Enabled = true;
}
catch (Exception ex)
{
MessagesLB.Items.Add(ex.Message);
}
}
As a first step I'm just trying to authenticate a user/check that it's in the live database, if so connect and fill out: Context.UserIdentifier so I can use this within the Hub. I understand that I probably need a middleware however I don't really know exactly how to test a connectionId/Jwt token or similar to get the user/connection.
Any nudge in the right direction would be appreciated.
If I understand your question you don't know where and how to generate a JWT token.
For me the JWT token should be generated from the server, your hub.
POST api/auth and in the playload you give login + SHA256 password and returns JWT token.
Once you checked the user auth is correct in you DB you can issue the token.
To generate a JWT token I use this piece of code.
public string GenerateToken(IConfiguration Config, DateTime? expire)
{
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, userName),
new Claim(JwtRegisteredClaimNames.Jti, _id),
new Claim(ClaimsIdentity.DefaultRoleClaimType, role)
};
// ClaimsIdentity.DefaultRoleClaimType
var bytes = Encoding.UTF8.GetBytes(Config["jwt:Secret"]);
var key = new SymmetricSecurityKey(bytes);
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
//Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;
var token = new JwtSecurityToken(
//Config.GetValue<string>("jwt:Issuer"),
//Config.GetValue<string>("jwt:Issuer") + "/ressources",
claims: claims,
expires: DateTime.Now.AddMinutes(Config.GetValue<int>("jwt:ExpireMinute")),
signingCredentials: creds);
return new JwtSecurityTokenHandler().WriteToken(token);
}
#edit
Look here to allow JWT for SignalR
https://learn.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-6.0
I also added this.
services.AddAuthorization(auth =>
{
auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser().Build());
});
The easiest solution would be to use something like IdentityServer to handle the authentication. It's a free solution, also .NET based which takes very little configuration effort to offer you simple client credentials authentication and generate the token for you.
I did basically exactly what you're asking here: A WinForms application connecting to my signalR hub application on a remote server, using Bearer token - but I also have OIDC/OAUTH implemented with third party user account login.
IdentityServer offers a great repository of full examples that showing you all the flow - and with just a few lines of code changed, you have a fullblown authentication system, which can be enhanced easily.
With IdentityServer you get everything, even the corresponding extension methods that enable your signalR hub application to create the claims principal (aka user) from the claims included within your token.
Here you'll find all the examples and docs:
https://github.com/IdentityServer/IdentityServer4
If you hit any walls, just reply here and I'll try to help.

CORS Error when React App tries to connect via SignalR to .NET CORE Web App having Windows Authentication enabled

I am trying to send data from React App to .NET Core Web App using SignalR.
The .NET Core Web App has Windows Authentication enabled. With Windows Authentication enabled, I am getting the CORS error when my React App tries to send message to .NET Core App via SignalR.
It works fine if I disable windows authentication and enable anonymous authentication.
Can you please help me with your valuable inputs to make the connection work?
React JS app code looks like below:
const hubConnection = new HubConnectionBuilder()
.withUrl(window.config.APP_URL, options)
.withAutomaticReconnect()
.build();
this.setState({ hubConnection }, () => {
this.state.hubConnection
.start()
.then(() => console.log("SignalR Connection started!"))
.catch((err) =>
console.log("SignalR Error while establishing connection :(", err)
);
});
}
sendMessage = () => {
console.log("sendMessage() Properties: ", this.props);
const signalRMessage = {
UserName: this.props.userName,
};
this.state.hubConnection
.invoke("SendMessage", signalRMessage)
.catch((err) => console.error(err));
};
I tried to explicitly add the ‘Access-Control-Allow-Origin’ header as shown below. But still I see the CORS error.
componentDidMount() {
let options = {
httpClient: {
post: (url, httpOptions) => {
// httpOptions.headers = {
// ...httpOptions.headers,
// "Access-Control-Allow-Origin": window.config.CORS_ALLOW_ORIGIN_URL,
// //"Access-Control-Allow-Methods": "POST, GET, HEAD",
// };
// httpOptions.method = "POST";
// httpOptions.url = url;
// return httpOptions;
const headers = {
...httpOptions.headers,
"Access-Control-Allow-Origin": window.config.CORS_ALLOW_ORIGIN_URL,
"Access-Control-Allow-Methods": "POST, GET, HEAD",
};
let newResponse = {};
return axios.post(url, {}, { headers }).then(response => {
return (newResponse = {
statusCode: response.status,
statusText: response.statusText,
content: JSON.stringify(response.data)
});
});
}
}
};
Below is the CORS error that I see in console logs at React App side:
enter image description here
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(IISDefaults.AuthenticationScheme);
services.AddRazorPages();
services.AddCors(options =>
{
options.AddPolicy("ClientPermissionPolicy", policy =>
{
policy.WithOrigins("https://xxxx.com")
.AllowAnyHeader()
.AllowAnyMethod().AllowCredentials();
});
});
...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCors("ClientPermissionPolicy");
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapHub<TapHub>("/hubs/tap");
});
}
Went through many posts here in stackoverflow and tried below but in vain.
In Configure():
app.UseCors(builder => builder.WithOrigins("https://xxxx.com").AllowAnyMethod().AllowAnyHeader().AllowCredentials());
In ConfigureServices():
services.AddCors(options =>
{
options.AddPolicy("ClientPermissionPolicy", policy =>
{
policy.WithOrigins("https://xxxxx.com")
.AllowAnyHeader()
.AllowAnyMethod().AllowCredentials()
.SetIsOriginAllowed((host) => true);
});
});
Tried AllowAnyOrigin()
Tried removing AllowCredentials()
Like I said above, it works fine if I disable windows authentication and enable anonymous authentication. The React App successfully connects to the hub endpoint in case of anonymous authentication. The CORS error comes into picture only when I enable windows authentication. I need Windows Authentication enabled for my requirement. Requesting you to help fix the issue.
Thanks!
A bit late, however I faced similar problem right now. Even with enabled CORS, it still rejected me. In the end, I found out that problem is in the
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
When I included CORS into this part, it started to work ... finally
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers().RequireCors("ClientPermissionPolicy");
});
You have to place UseCors between UseRouting and UseAutorization
app.UseRouting();
app.UseCors("ClientPermissionPolicy");
app.UseAuthorization();
and maybe you can try to move AddCors to the top of ConfigureServices method
Just the test pourposes I would use this
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(o => o.AddPolicy("ClientPermissionPolicy", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
}));
.....
}
only if it works, I would try to use specific origins.
For those using .NET Core 3.1, here is a COMPLETE solution (front-end to back-end):
My problem: When I enabled the windows authentication on my web API, I could not do fetch calls from my react app to my .NET Core 3.1 web API, CORS was freaking out. With Anonymous authentication it worked, but not when windows authentication is enabled.
1.launchSettings.json
this will be used only for your dev environnment, make sure windows auth is also enabled in IIS on your prod server.
{
"iisSettings": {
"windowsAuthentication": true,
"anonymousAuthentication": false,
"iisExpress": {
"applicationUrl": "http://localhost:58747",
"sslPort": 0
}
},
{... more settings if any}
}
2.Startup.cs:
CORS policy is enabled here. The order of methods is important here. Also, you don't need to set those in a web.config
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy", //give it the name you want
builder =>
{
builder.WithOrigins( "http://localhost:3000", //dev site
"production web site"
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
//database services here
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
// global policy same name as in the ConfigureServices()
app.UseCors("CorsPolicy");
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
3.Controller(s):
using Microsoft.AspNetCore.Cors;
... your other usings
namespace ProjectTest.Controllers
{
[ApiController]
[EnableCors("CorsPolicy")] //THIS HERE needs to be the same name as set in your startup.cs
[Route("[controller]")]
public class FooController:Controller
{
[HttpGet("getTest")]
public JsonResult GetTest()
{
return Json("bar");
}
}
}
4.React Component fetch call example:
The "credential: 'include'" is the secret
await fetch('http://localhost:3000/Foo/getTest', {
method: 'GET',
credentials: 'include'
}).then(resp => resp.json());
As mentioned here, the problem takes place because:
IIS's Windows Auth happens before ASP.NET Core runs, the ASP.NET Core
CORS implementation isn't able to process the OPTIONS request (because
the browser won't send your Windows credentials with it). The only
real solution here is to use the IIS CORS module since it runs before
Windows Authentication and can process the unauthenticated CORS
request.

Call Api from blazor and pass Auth token

I have a blazor webassembly project that gets created from https://github.com/Azure-Samples/ms-identity-blazor-wasm/tree/main/WebApp-graph-user/Call-MSGraph.
Its basically the project that gets created when you use the .net core template for a Blazor application that uses authentication via AD B2B
dotnet new blazorwasm -au SingleOrg --client-id "{CLIENT ID}" -o {APP NAME} --tenant-id "{TENANT ID}"
I was then able to call graph.api when the user logged in. I then tried to call my own Api with that authentication as described in https://learn.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/additional-scenarios?view=aspnetcore-3.1.
I used
builder.Services.AddHttpClient<ITestDataService, TestDataService>(
client => client.BaseAddress = new Uri("https://localhost:44342/"))
.AddHttpMessageHandler(x =>
{
var handler = x.GetRequiredService<AuthorizationMessageHandler>()
.ConfigureHandler(new[] { "https://localhost:44342/" },
scopes: new[] { "https://graph.microsoft.com/User.Read" });
return handler;
});
I can see that a token is attached when calling the Api but authentication fails (401). The api is generated from Visual Studio templates for B2B AD and uses the configuration that is also used for the Blazor application.
This is its Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
.AddAzureADBearer(options => Configuration.Bind("AzureAd", options));
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseCors(policy =>
policy.WithOrigins("http://localhost:5000",
"https://localhost:5001")
.AllowAnyMethod()
.WithHeaders(HeaderNames.ContentType,
HeaderNames.Authorization,
"x-custom-header")
.AllowCredentials());
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
Do you have any idea what is missing?
The complete source is available at github https://github.com/mathiasfritsch/blazor-calls-api
If you want to call Microsoft graph and your custom API in one blazor webassembly project, we can implement it by creating different HTTP client to call different API
For example
Register a server API app
Register an AAD app for the Server API app
Expose an API
Register a client app
Register a client app
Enable Implicit grant flow
Add API permissions. (Graph API permissions and API app permissions)
Configure API app
Please add the following code in Startup.cs
public void ConfigureServices(IServiceCollection services)
{
JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
services.AddCors(options =>
{
options.AddDefaultPolicy(
builder => builder.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod());
});
services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
.AddAzureADBearer(options => Configuration.Bind("AzureAd", options));
services.Configure<JwtBearerOptions>(AzureADDefaults.JwtBearerAuthenticationScheme, options =>
{
options.Authority += "/v2.0";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuers = new[] {
$"https://sts.windows.net/{Configuration["AzureAD:TenantId"]}/",
$"https://login.microsoftonline.com/{Configuration["AzureAD:TenantId"]}/v2.0"
},
RoleClaimType = "roles",
// The web API accepts as audiences both the Client ID (options.Audience) and api://{ClientID}.
ValidAudiences = new[]
{
options.Audience,
$"api://{options.Audience}"
}
};
});
....
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.OAuthClientId(Configuration["Swagger:ClientId"]);
c.OAuthScopeSeparator(" ");
c.OAuthAppName("Protected Api");
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
Configure Client APP
Create custom AuthorizationMessageHandler for Graph API and custom API
// custom API
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
public class CustomAuthorizationMessageHandler : AuthorizationMessageHandler
{
public CustomAuthorizationMessageHandler(IAccessTokenProvider provider,
NavigationManager navigationManager)
: base(provider, navigationManager)
{
ConfigureHandler(
authorizedUrls: new[] { "https://localhost:44300/" },
scopes: new[] { "the API app scope" });
}
}
//Graph API
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
public class GraphAuthorizationMessageHandler : AuthorizationMessageHandler
{
public GraphAuthorizationMessageHandler(IAccessTokenProvider provider,
NavigationManager navigationManager)
: base(provider, navigationManager)
{
ConfigureHandler(
authorizedUrls: new[] { "https://graph.microsoft.com/" },
scopes: new[] { "https://graph.microsoft.com/User.Read" });
}
}
Add the following code to the program.cs
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("app");
builder.Services.AddScoped<CustomAuthorizationMessageHandler>();
builder.Services.AddScoped<GraphAuthorizationMessageHandler>();
// register HTTP client to call our own api
builder.Services.AddHttpClient("MyAPI", client => client.BaseAddress = new Uri("https://localhost:44300/"))
.AddHttpMessageHandler<CustomAuthorizationMessageHandler>();
// register HTTP client to call graph api
builder.Services.AddHttpClient("GraphAPI", client => client.BaseAddress = new Uri("https://graph.microsoft.com/"))
.AddHttpMessageHandler<GraphAuthorizationMessageHandler>();
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
options.ProviderOptions.DefaultAccessTokenScopes.Add("<the API app scope>");
options.ProviderOptions.AdditionalScopesToConsent.Add("https://graph.microsoft.com/User.Read");
});
await builder.Build().RunAsync();
}
}
Call the api
#inject IHttpClientFactory _clientFactory
var httpClient = _clientFactory.CreateClient("<the client name you register>");
await apiClient.GetStringAsync("path");

Razor Pages Application not redirecting to IdentityServer4

I am having trouble getting my .NET Core site to redirect to my identity server for authentication when accessing a page. When I run the site locally this works fine. However, when I deploy the site it no longer works.
Here is the code for the application startup (just for the authentication)
public void ConfigureServices(IserviceCollection services) {
services
.AddAuthentication(options => {
options.DefaultScheme => "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options => {
options.Authority = "IdentityServerUrl";
options.ClientId = "clientId";
options.ResponseType = "code";
options.SaveTokens = true;
options.Scope.Add("scope");
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints => endpoints.MapRazorPages().RequireAuthorization());
}
And then in our page:
public class IndexModel : PageModel {
public async Task OnGetAsync() {
var authentication = await this.HttpContext.AuthenticateAsync();
var accessTokenJwt = authentication.Properties.Items[".Token.access_token"];
}
}
This is all working correctly when run locally. It is successfully redirecting to the identity server, logging in, returning to the application, and setting the cookies.
However, when this is deployed to a webserver it is not working at all. The await this.HttpContext.AuthenticateAsync(); is called and just immediately returns null. This results in an exception being thrown on the line below.
Any help with this would be much appreciated. Thanks in advance.

Resources