44using Microsoft . AspNetCore . Http ;
55using Microsoft . AspNetCore . Identity ;
66using Microsoft . AspNetCore . Mvc ;
7+ using Microsoft . IdentityModel . Tokens ;
78using OpenIddict . Abstractions ;
89using OpenIddict . Server . AspNetCore ;
910using System ;
@@ -16,10 +17,14 @@ namespace ClassifiedAds.IdentityServer.Controllers
1617{
1718 public class AuthorizationController : Controller
1819 {
20+ private readonly UserManager < User > _userManager ;
1921 private readonly SignInManager < User > _signInManager ;
2022
21- public AuthorizationController ( SignInManager < User > signInManager )
23+ public AuthorizationController (
24+ UserManager < User > userManager ,
25+ SignInManager < User > signInManager )
2226 {
27+ _userManager = userManager ;
2328 _signInManager = signInManager ;
2429 }
2530
@@ -69,8 +74,7 @@ public async Task<IActionResult> Authorize()
6974 [ HttpPost ( "~/connect/token" ) ]
7075 public async Task < IActionResult > Exchange ( )
7176 {
72- var request = HttpContext . GetOpenIddictServerRequest ( ) ??
73- throw new InvalidOperationException ( "The OpenID Connect request cannot be retrieved." ) ;
77+ var request = HttpContext . GetOpenIddictServerRequest ( ) ?? throw new InvalidOperationException ( "The OpenID Connect request cannot be retrieved." ) ;
7478
7579 ClaimsPrincipal claimsPrincipal ;
7680
@@ -101,6 +105,57 @@ public async Task<IActionResult> Exchange()
101105 // Retrieve the claims principal stored in the refresh token.
102106 claimsPrincipal = ( await HttpContext . AuthenticateAsync ( OpenIddictServerAspNetCoreDefaults . AuthenticationScheme ) ) . Principal ;
103107 }
108+ else if ( request . IsPasswordGrantType ( ) )
109+ {
110+ var user = await _userManager . FindByNameAsync ( request . Username ) ;
111+ if ( user == null )
112+ {
113+ var properties = new AuthenticationProperties ( new Dictionary < string , string >
114+ {
115+ [ OpenIddictServerAspNetCoreConstants . Properties . Error ] = OpenIddictConstants . Errors . InvalidGrant ,
116+ [ OpenIddictServerAspNetCoreConstants . Properties . ErrorDescription ] = "The username/password couple is invalid."
117+ } ) ;
118+
119+ return Forbid ( properties , OpenIddictServerAspNetCoreDefaults . AuthenticationScheme ) ;
120+ }
121+
122+ // Validate the username/password parameters and ensure the account is not locked out.
123+ var result = await _signInManager . CheckPasswordSignInAsync ( user , request . Password , lockoutOnFailure : true ) ;
124+ if ( ! result . Succeeded )
125+ {
126+ var properties = new AuthenticationProperties ( new Dictionary < string , string >
127+ {
128+ [ OpenIddictServerAspNetCoreConstants . Properties . Error ] = OpenIddictConstants . Errors . InvalidGrant ,
129+ [ OpenIddictServerAspNetCoreConstants . Properties . ErrorDescription ] = "The username/password couple is invalid."
130+ } ) ;
131+
132+ return Forbid ( properties , OpenIddictServerAspNetCoreDefaults . AuthenticationScheme ) ;
133+ }
134+
135+ // Create the claims-based identity that will be used by OpenIddict to generate tokens.
136+ var identity = new ClaimsIdentity (
137+ authenticationType : TokenValidationParameters . DefaultAuthenticationType ,
138+ nameType : OpenIddictConstants . Claims . Name ,
139+ roleType : OpenIddictConstants . Claims . Role ) ;
140+
141+ // Add the claims that will be persisted in the tokens.
142+ identity . SetClaim ( OpenIddictConstants . Claims . Subject , await _userManager . GetUserIdAsync ( user ) )
143+ . SetClaim ( OpenIddictConstants . Claims . Email , await _userManager . GetEmailAsync ( user ) )
144+ . SetClaim ( OpenIddictConstants . Claims . Name , await _userManager . GetUserNameAsync ( user ) ) ;
145+
146+ // Set the list of scopes granted to the client application.
147+ identity . SetScopes ( new [ ]
148+ {
149+ OpenIddictConstants . Scopes . OpenId ,
150+ OpenIddictConstants . Scopes . Email ,
151+ OpenIddictConstants . Scopes . Profile ,
152+ OpenIddictConstants . Scopes . Roles
153+ } . Intersect ( request . GetScopes ( ) ) ) ;
154+
155+ identity . SetDestinations ( GetDestinations ) ;
156+
157+ return SignIn ( new ClaimsPrincipal ( identity ) , OpenIddictServerAspNetCoreDefaults . AuthenticationScheme ) ;
158+ }
104159 else
105160 {
106161 throw new InvalidOperationException ( "The specified grant type is not supported." ) ;
@@ -134,5 +189,51 @@ public async Task<IActionResult> LogoutPost()
134189 } ) ;
135190 }
136191
192+ private static IEnumerable < string > GetDestinations ( Claim claim )
193+ {
194+ // Note: by default, claims are NOT automatically included in the access and identity tokens.
195+ // To allow OpenIddict to serialize them, you must attach them a destination, that specifies
196+ // whether they should be included in access tokens, in identity tokens or in both.
197+
198+ switch ( claim . Type )
199+ {
200+ case OpenIddictConstants . Claims . Name :
201+ yield return OpenIddictConstants . Destinations . AccessToken ;
202+
203+ if ( claim . Subject . HasScope ( OpenIddictConstants . Scopes . Profile ) )
204+ {
205+ yield return OpenIddictConstants . Destinations . IdentityToken ;
206+ }
207+
208+ yield break ;
209+
210+ case OpenIddictConstants . Claims . Email :
211+ yield return OpenIddictConstants . Destinations . AccessToken ;
212+
213+ if ( claim . Subject . HasScope ( OpenIddictConstants . Scopes . Email ) )
214+ {
215+ yield return OpenIddictConstants . Destinations . IdentityToken ;
216+ }
217+
218+ yield break ;
219+
220+ case OpenIddictConstants . Claims . Role :
221+ yield return OpenIddictConstants . Destinations . AccessToken ;
222+
223+ if ( claim . Subject . HasScope ( OpenIddictConstants . Scopes . Roles ) )
224+ {
225+ yield return OpenIddictConstants . Destinations . IdentityToken ;
226+ }
227+
228+ yield break ;
229+
230+ // Never include the security stamp in the access and identity tokens, as it's a secret value.
231+ case "AspNet.Identity.SecurityStamp" : yield break ;
232+
233+ default :
234+ yield return OpenIddictConstants . Destinations . AccessToken ;
235+ yield break ;
236+ }
237+ }
137238 }
138239}
0 commit comments