Skip to content

Commit

Permalink
(#181) Adding OpenIddict
Browse files Browse the repository at this point in the history
  • Loading branch information
phongnguyend committed Feb 13, 2023
1 parent 7a762f2 commit 1d87f65
Show file tree
Hide file tree
Showing 22 changed files with 485 additions and 109 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
$date_now = Get-Date
$extended_date = $date_now.AddYears(3)
$extended_date = $date_now.AddYears(10)
$cert = New-SelfSignedCertificate -certstorelocation cert:\localmachine\my -dnsname classifiedads.identityserver -notafter $extended_date
$pwd = ConvertTo-SecureString -String 'password1234' -Force -AsPlainText
$path = 'cert:\localMachine\my\' + $cert.thumbprint
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ namespace ClassifiedAds.IdentityServer.ConfigurationOptions
{
public class IdentityServerOptions
{
public CertificateOption Certificate { get; set; }
public CertificateOption EncryptionCertificate { get; set; }

public CertificateOption SigningCertificate { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using OpenIddict.Abstractions;
using OpenIddict.Server.AspNetCore;
using System;
Expand All @@ -16,10 +17,14 @@ namespace ClassifiedAds.IdentityServer.Controllers
{
public class AuthorizationController : Controller
{
private readonly UserManager<User> _userManager;
private readonly SignInManager<User> _signInManager;

public AuthorizationController(SignInManager<User> signInManager)
public AuthorizationController(
UserManager<User> userManager,
SignInManager<User> signInManager)
{
_userManager = userManager;
_signInManager = signInManager;
}

Expand Down Expand Up @@ -69,8 +74,7 @@ public async Task<IActionResult> Authorize()
[HttpPost("~/connect/token")]
public async Task<IActionResult> Exchange()
{
var request = HttpContext.GetOpenIddictServerRequest() ??
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved.");
var request = HttpContext.GetOpenIddictServerRequest() ?? throw new InvalidOperationException("The OpenID Connect request cannot be retrieved.");

ClaimsPrincipal claimsPrincipal;

Expand Down Expand Up @@ -101,6 +105,57 @@ public async Task<IActionResult> Exchange()
// Retrieve the claims principal stored in the refresh token.
claimsPrincipal = (await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)).Principal;
}
else if (request.IsPasswordGrantType())
{
var user = await _userManager.FindByNameAsync(request.Username);
if (user == null)
{
var properties = new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The username/password couple is invalid."
});

return Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
}

// Validate the username/password parameters and ensure the account is not locked out.
var result = await _signInManager.CheckPasswordSignInAsync(user, request.Password, lockoutOnFailure: true);
if (!result.Succeeded)
{
var properties = new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The username/password couple is invalid."
});

return Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
}

// Create the claims-based identity that will be used by OpenIddict to generate tokens.
var identity = new ClaimsIdentity(
authenticationType: TokenValidationParameters.DefaultAuthenticationType,
nameType: OpenIddictConstants.Claims.Name,
roleType: OpenIddictConstants.Claims.Role);

// Add the claims that will be persisted in the tokens.
identity.SetClaim(OpenIddictConstants.Claims.Subject, await _userManager.GetUserIdAsync(user))
.SetClaim(OpenIddictConstants.Claims.Email, await _userManager.GetEmailAsync(user))
.SetClaim(OpenIddictConstants.Claims.Name, await _userManager.GetUserNameAsync(user));

// Set the list of scopes granted to the client application.
identity.SetScopes(new[]
{
OpenIddictConstants.Scopes.OpenId,
OpenIddictConstants.Scopes.Email,
OpenIddictConstants.Scopes.Profile,
OpenIddictConstants.Scopes.Roles
}.Intersect(request.GetScopes()));

identity.SetDestinations(GetDestinations);

return SignIn(new ClaimsPrincipal(identity), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
}
else
{
throw new InvalidOperationException("The specified grant type is not supported.");
Expand Down Expand Up @@ -134,5 +189,51 @@ public async Task<IActionResult> LogoutPost()
});
}

private static IEnumerable<string> GetDestinations(Claim claim)
{
// Note: by default, claims are NOT automatically included in the access and identity tokens.
// To allow OpenIddict to serialize them, you must attach them a destination, that specifies
// whether they should be included in access tokens, in identity tokens or in both.

switch (claim.Type)
{
case OpenIddictConstants.Claims.Name:
yield return OpenIddictConstants.Destinations.AccessToken;

if (claim.Subject.HasScope(OpenIddictConstants.Scopes.Profile))
{
yield return OpenIddictConstants.Destinations.IdentityToken;
}

yield break;

case OpenIddictConstants.Claims.Email:
yield return OpenIddictConstants.Destinations.AccessToken;

if (claim.Subject.HasScope(OpenIddictConstants.Scopes.Email))
{
yield return OpenIddictConstants.Destinations.IdentityToken;
}

yield break;

case OpenIddictConstants.Claims.Role:
yield return OpenIddictConstants.Destinations.AccessToken;

if (claim.Subject.HasScope(OpenIddictConstants.Scopes.Roles))
{
yield return OpenIddictConstants.Destinations.IdentityToken;
}

yield break;

// Never include the security stamp in the access and identity tokens, as it's a secret value.
case "AspNet.Identity.SecurityStamp": yield break;

default:
yield return OpenIddictConstants.Destinations.AccessToken;
yield break;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using IdentityModel;
using OpenIddict.Abstractions;
using System.Security.Claims;

namespace ClassifiedAds.IdentityServer.Extensions
Expand All @@ -13,7 +13,7 @@ public static string GetDisplayName(this ClaimsPrincipal principal)
return name;
}

var sub = principal.FindFirst(JwtClaimTypes.Subject);
var sub = principal.FindFirst(OpenIddictConstants.Claims.Subject);
if (sub != null)
{
return sub.Value;
Expand Down
Loading

0 comments on commit 1d87f65

Please sign in to comment.