Scenario:
Setup OAuth based authentication/authorization for ASP.NET core site
Solution:
Identity Server 4 is authentication and authorization package using JSON web tokens (JWT) and it implements OAuth 2.0 specs.
1. NugetPackage -> dotnet add package IdentityServer4
2. IValidateAppUser
1
2
3
4 | public interface IValidateAppUser
{
User GetUserInfo(string name, string password);
} |
3. ValidateAppUser
1
2
3
4
5 | public User GetUserInfo(string name, string password)
{
//call app user repo
return new User();
} |
3. IdentityConfiguration
| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66 | public static IEnumerable<IdentityResource> IdentityResources =>
new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
};
public static IEnumerable<ApiResource> ApiResources()
{
return new List<ApiResource>
{
new ApiResource("ApiResourceId", "My App")
{
ApiSecrets = new List<Secret>
{
new Secret("ApiResourceKey".Sha256())
},
Scopes =
{
"offline_access",
},
UserClaims = new[] {"sub", "userId", "name", "email", "role", "returnUrl"},
}
};
}
public static IEnumerable<Client> Clients()
{
return new[]
{
new Client
{
ClientId = "ClientId",
ClientSecrets =
{new Secret("ClientSecret".Sha256())},
AllowedGrantTypes = GrantTypes.Code,
RedirectUris = new List<string>(),
RequireConsent = false,
RequirePkce = false,
PostLogoutRedirectUris = new List<string>(),
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.OfflineAccess
},
AllowOfflineAccess = true,
AllowAccessTokensViaBrowser = true,
AccessTokenLifetime = 5,
AccessTokenType = AccessTokenType.Jwt,
RefreshTokenUsage = TokenUsage.ReUse,
RefreshTokenExpiration = TokenExpiration.Sliding,
}
};
}
public static IEnumerable<ApiScope> ApiScopes =>
new ApiScope[]
{
new ApiScope("offline_access", "My App"),
};
} |
|
4.Startup.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 | public void ConfigureServices(IServiceCollection services)
{
//Validate user
services.AddTransient<IValidateAppUser, ValidateAppUser>();
//Repo to get user info
services.AddTransient<IAppUserRepo, AppUserRepo>();
services.AddIdentityServer()
.AddSigningCredential(X509Certificate2.CreateFromEncryptedPem("", new ReadOnlySpan<char>(), "test"))
.AddInMemoryIdentityResources(IdentityConfiguration.IdentityResources)
.AddInMemoryApiScopes(IdentityConfiguration.ApiScopes)
.AddInMemoryApiResources(IdentityConfiguration.ApiResources())
.AddInMemoryClients(IdentityConfiguration.Clients());
services.AddTransient<IPersistedGrantStore, GrantStore>();
services.AddOptions<KeyManagementOptions>()
.Configure<IServiceScopeFactory>((options, factory) =>
{
options.XmlRepository = new DBKeyRepository(factory);
});
services.AddAuthentication("OAuthCookie")
.AddCookie("OAuthCookie", options =>
{
options.ExpireTimeSpan =
new TimeSpan(10, 0, 0);
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseIdentityServer();
app.UseAuthentication();
app.UseMiddleware<Handler>();
app.UseAuthorization();
} |
|
|
5. AccountController
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginInputModel model, string button)
{
.....
if (ModelState.IsValid)
{
var user = await _validateUser.GetUserInfo(model.Username, model.Password);
if (user != null)
{
var returnUrl = string.Empty;
if (model.ReturnUrl != null)
{
returnUrl = HttpUtility.ParseQueryString(model.ReturnUrl)["nonce"];
}
var claims = (new[]
{
new Claim("sub", user.UserId),
new Claim("user", user.UserId),
new Claim("name", user.Name),
new Claim("roles", user.Roles),
new Claim("returnUrl", (string.IsNullOrEmpty(returnUrl))
});
var userIdentity = new ClaimsIdentity(claims, "Claims");
var userPrincipal = new ClaimsPrincipal(new[] { userIdentity });
await HttpContext.SignInAsync(userPrincipal).ConfigureAwait(false);
}
}
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Logout(LogoutInputModel model)
{
// build a model so the logged out page knows what to display
var vm = await BuildLoggedOutViewModelAsync(model.LogoutId).ConfigureAwait(false);
...
vm.PostLogoutRedirectUri = "PostLogoutRedirectUri";
if (User?.Identity.IsAuthenticated == true)
{
..
}
return View("", vm);
}
private async Task<LoggedOutViewModel> BuildLoggedOutViewModelAsync(string logoutId)
{
..
if (User?.Identity.IsAuthenticated == true)
{
..
}
return vm;
}
No comments:
Post a Comment