I am creating an application using .net core 3.1 with Identity server 4 OpenID connect with Razor Pages
Project Structure:
- Api
- Auth Server (using identity server 4 template "dotnet new is4inmem")
- MvcClient (Razor pages)
Startup.cs (Auth server project)
public class Startup
{
public IWebHostEnvironment Environment { get; }
public IConfiguration Configuration { get; }
public Startup(IWebHostEnvironment environment, IConfiguration configuration)
{
Environment = environment;
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
// configures IIS out-of-proc settings (see https://github.com/aspnet/AspNetCore/issues/14882)
services.Configure<IISOptions>(iis =>
{
iis.AuthenticationDisplayName = "Windows";
iis.AutomaticAuthentication = false;
});
// configures IIS in-proc settings
services.Configure<IISServerOptions>(iis =>
{
iis.AuthenticationDisplayName = "Windows";
iis.AutomaticAuthentication = false;
});
var builder = services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
})
.AddTestUsers(TestUsers.Users);
// in-memory, code config
builder.AddInMemoryIdentityResources(Config.Ids);
builder.AddInMemoryApiResources(Config.Apis);
builder.AddInMemoryClients(Config.Clients);
// or in-memory, json config
//builder.AddInMemoryIdentityResources(Configuration.GetSection("IdentityResources"));
//builder.AddInMemoryApiResources(Configuration.GetSection("ApiResources"));
//builder.AddInMemoryClients(Configuration.GetSection("clients"));
// not recommended for production - you need to store your key material somewhere secure
builder.AddDeveloperSigningCredential();
services.AddAuthentication();
}
public void Configure(IApplicationBuilder app)
{
if (Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
}
Config.cs (Auth server project)
public static class Config
{
public static IEnumerable<IdentityResource> Ids =>
new IdentityResource[]
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
};
public static IEnumerable<ApiResource> Apis =>
new ApiResource[]
{
new ApiResource("Sample.Api", "My Sample Api #1")
};
public static IEnumerable<Client> Clients =>
new Client[]
{
// client credentials flow client
new Client
{
ClientId = "client",
ClientName = "Client Credentials Client",
AllowedGrantTypes = GrantTypes.ClientCredentials,
ClientSecrets = { new Secret("511536EF-F270-4058-80CA-1C89C192F69A".Sha256()) },
AllowedScopes = { "Sample.Api" }
},
// MVC client using code flow + pkce
new Client
{
ClientId = "mvc",
ClientName = "MVC Client",
AllowedGrantTypes = GrantTypes.CodeAndClientCredentials,
RequirePkce = true,
ClientSecrets = { new Secret("49C1A7E1-0C79-4A89-A3D6-A37998FB86B0".Sha256()) },
RedirectUris = { "http://localhost:5003/signin-oidc" },
FrontChannelLogoutUri = "http://localhost:5003/signout-oidc",
PostLogoutRedirectUris = { "http://localhost:5003/signout-callback-oidc" },
AllowOfflineAccess = true,
RequireConsent= false,
AllowedScopes = { "openid", "profile", "Sample.Api" }
},
// SPA client using code flow + pkce
new Client
{
ClientId = "spa",
ClientName = "SPA Client",
ClientUri = "http://identityserver.io",
AllowedGrantTypes = GrantTypes.Code,
RequirePkce = true,
RequireClientSecret = false,
RedirectUris =
{
"http://localhost:5002/index.html",
"http://localhost:5002/callback.html",
"http://localhost:5002/silent.html",
"http://localhost:5002/popup.html",
},
PostLogoutRedirectUris = { "http://localhost:5002/index.html" },
AllowedCorsOrigins = { "http://localhost:5002" },
AllowedScopes = { "openid", "profile", "Sample.Api" }
}
};
}
Startup.cs (From Mvc client)
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => false;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication(options =>
{
options.DefaultScheme = "cookie";
options.DefaultChallengeScheme = "oidc";
}).AddCookie("cookie")
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "cookie";
options.Authority = "http://localhost:5000/";
options.RequireHttpsMetadata = false;
options.ClientId = "mvc";
options.ClientSecret = "49C1A7E1-0C79-4A89-A3D6-A37998FB86B0";
options.SaveTokens = true;
options.ResponseType = "code";
options.Scope.Clear();
options.Scope.Add("Sample.Api");
options.Scope.Add("openid");
options.Scope.Add("offline_access");
});
services.AddRazorPages();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
}
Privacy.cshtml.cs (Razor page with Authorize attribute)
[Authorize]
public class PrivacyModel : PageModel
{
private readonly ILogger<PrivacyModel> _logger;
public PrivacyModel(ILogger<PrivacyModel> logger)
{
_logger = logger;
}
public void OnGet()
{
}
}
Everything is working fine I am able to login and access my Privacy authroize page but I have few questions here.
How can I get the logged in username and add logout button at the top of navbar ? (I want to add this on _layout.cshtml in Mvc client project not in Auth server _layout.cshtml).
Also that will be helpful if somebody can tell me how do I add Register functionality ? (same like above I want to add this Register button on the MVC client _layout.cshtml too).
I tried this as well but it is not working.
Here is the code from MVC project.
/shared/_Layout.cshtml (Mvc project)
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container">
<a class="navbar-brand" asp-area="" asp-page="/Index">Sample.MvcClient</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>
</li>
@*How can I add logged in username, Register button and Logout button here*@
@if (User.Identity.IsAuthenticated)
{
<li class="nav-item">
<a id="manage" class="nav-link text-dark">Hello @User.Identity.Name</a>
</li>
}
</ul>
</div>
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>
_Layout.cshtml (Auth Project) - here I have commented the navbar because I want to display the user name and Logout and Register button on MVC client Layout not here.
@*<div class="navbar navbar-inverse navbar-fixed-top">*@
<div class="container-fluid">
@*<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a href="~/">
<span class="navbar-brand">
<img src="~/icon.png" class="icon-banner">
IdentityServer4
</span>
</a>
</div>*@
@if (!string.IsNullOrWhiteSpace(name))
{
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">@name <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a asp-action="Logout" asp-controller="Account">Logout</a></li>
</ul>
</li>
</ul>
}
</div>
@*</div>*@
<div class="container body-content">
@RenderBody()
</div>
Can somebody help me out on this ?
Thanks in advance