1
+ // Copyright (c) Duende Software. All rights reserved.
2
+ // See LICENSE in the project root for license information.
3
+
4
+ using Duende . IdentityServer ;
1
5
using Duende . IdentityServer . Events ;
2
6
using Duende . IdentityServer . Models ;
3
7
using Duende . IdentityServer . Services ;
@@ -22,10 +26,10 @@ public class Index : PageModel
22
26
private readonly IAuthenticationSchemeProvider _schemeProvider ;
23
27
private readonly IIdentityProviderStore _identityProviderStore ;
24
28
25
- public ViewModel View { get ; set ; }
29
+ public ViewModel View { get ; set ; } = default ! ;
26
30
27
31
[ BindProperty ]
28
- public InputModel Input { get ; set ; }
32
+ public InputModel Input { get ; set ; } = default ! ;
29
33
30
34
public Index (
31
35
IIdentityServerInteractionService interaction ,
@@ -42,8 +46,8 @@ public Index(
42
46
_identityProviderStore = identityProviderStore ;
43
47
_events = events ;
44
48
}
45
-
46
- public async Task < IActionResult > OnGet ( string returnUrl )
49
+
50
+ public async Task < IActionResult > OnGet ( string ? returnUrl )
47
51
{
48
52
await BuildModelAsync ( returnUrl ) ;
49
53
@@ -66,6 +70,9 @@ public async Task<IActionResult> OnPost()
66
70
{
67
71
if ( context != null )
68
72
{
73
+ // This "can't happen", because if the ReturnUrl was null, then the context would be null
74
+ ArgumentNullException . ThrowIfNull ( Input . ReturnUrl , nameof ( Input . ReturnUrl ) ) ;
75
+
69
76
// if the user cancels, send a result back into IdentityServer as if they
70
77
// denied the consent (even if this client does not require consent).
71
78
// this will send back an access denied OIDC error response to the client.
@@ -79,7 +86,7 @@ public async Task<IActionResult> OnPost()
79
86
return this . LoadingPage ( Input . ReturnUrl ) ;
80
87
}
81
88
82
- return Redirect ( Input . ReturnUrl ) ;
89
+ return Redirect ( Input . ReturnUrl ?? "~/" ) ;
83
90
}
84
91
else
85
92
{
@@ -90,14 +97,18 @@ public async Task<IActionResult> OnPost()
90
97
91
98
if ( ModelState . IsValid )
92
99
{
93
- var result = await _signInManager . PasswordSignInAsync ( Input . Username , Input . Password , Input . RememberLogin , lockoutOnFailure : true ) ;
100
+ var result = await _signInManager . PasswordSignInAsync ( Input . Username ! , Input . Password ! , Input . RememberLogin , lockoutOnFailure : true ) ;
94
101
if ( result . Succeeded )
95
102
{
96
- var user = await _userManager . FindByNameAsync ( Input . Username ) ;
97
- await _events . RaiseAsync ( new UserLoginSuccessEvent ( user . UserName , user . Id , user . UserName , clientId : context ? . Client . ClientId ) ) ;
103
+ var user = await _userManager . FindByNameAsync ( Input . Username ! ) ;
104
+ await _events . RaiseAsync ( new UserLoginSuccessEvent ( user ! . UserName , user . Id , user . UserName , clientId : context ? . Client . ClientId ) ) ;
105
+ Telemetry . Metrics . UserLogin ( context ? . Client . ClientId , IdentityServerConstants . LocalIdentityProvider ) ;
98
106
99
107
if ( context != null )
100
108
{
109
+ // This "can't happen", because if the ReturnUrl was null, then the context would be null
110
+ ArgumentNullException . ThrowIfNull ( Input . ReturnUrl , nameof ( Input . ReturnUrl ) ) ;
111
+
101
112
if ( context . IsNativeClient ( ) )
102
113
{
103
114
// The client is native, so this change in how to
@@ -106,7 +117,7 @@ public async Task<IActionResult> OnPost()
106
117
}
107
118
108
119
// we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null
109
- return Redirect ( Input . ReturnUrl ) ;
120
+ return Redirect ( Input . ReturnUrl ?? "~/" ) ;
110
121
}
111
122
112
123
// request for a local page
@@ -121,20 +132,22 @@ public async Task<IActionResult> OnPost()
121
132
else
122
133
{
123
134
// user might have clicked on a malicious link - should be logged
124
- throw new Exception ( "invalid return URL" ) ;
135
+ throw new ArgumentException ( "invalid return URL" ) ;
125
136
}
126
137
}
127
138
128
- await _events . RaiseAsync ( new UserLoginFailureEvent ( Input . Username , "invalid credentials" , clientId : context ? . Client . ClientId ) ) ;
139
+ const string error = "invalid credentials" ;
140
+ await _events . RaiseAsync ( new UserLoginFailureEvent ( Input . Username , error , clientId : context ? . Client . ClientId ) ) ;
141
+ Telemetry . Metrics . UserLoginFailure ( context ? . Client . ClientId , IdentityServerConstants . LocalIdentityProvider , error ) ;
129
142
ModelState . AddModelError ( string . Empty , LoginOptions . InvalidCredentialsErrorMessage ) ;
130
143
}
131
144
132
145
// something went wrong, show form with error
133
146
await BuildModelAsync ( Input . ReturnUrl ) ;
134
147
return Page ( ) ;
135
148
}
136
-
137
- private async Task BuildModelAsync ( string returnUrl )
149
+
150
+ private async Task BuildModelAsync ( string ? returnUrl )
138
151
{
139
152
Input = new InputModel
140
153
{
@@ -152,11 +165,11 @@ private async Task BuildModelAsync(string returnUrl)
152
165
EnableLocalLogin = local ,
153
166
} ;
154
167
155
- Input . Username = context ? . LoginHint ;
168
+ Input . Username = context . LoginHint ;
156
169
157
170
if ( ! local )
158
171
{
159
- View . ExternalProviders = new [ ] { new ViewModel . ExternalProvider { AuthenticationScheme = context . IdP } } ;
172
+ View . ExternalProviders = new [ ] { new ViewModel . ExternalProvider ( authenticationScheme : context . IdP ) } ;
160
173
}
161
174
162
175
return ;
@@ -167,27 +180,27 @@ private async Task BuildModelAsync(string returnUrl)
167
180
var providers = schemes
168
181
. Where ( x => x . DisplayName != null )
169
182
. Select ( x => new ViewModel . ExternalProvider
170
- {
171
- DisplayName = x . DisplayName ?? x . Name ,
172
- AuthenticationScheme = x . Name
173
- } ) . ToList ( ) ;
183
+ (
184
+ authenticationScheme : x . Name ,
185
+ displayName : x . DisplayName ?? x . Name
186
+ ) ) . ToList ( ) ;
174
187
175
- var dyanmicSchemes = ( await _identityProviderStore . GetAllSchemeNamesAsync ( ) )
188
+ var dynamicSchemes = ( await _identityProviderStore . GetAllSchemeNamesAsync ( ) )
176
189
. Where ( x => x . Enabled )
177
190
. Select ( x => new ViewModel . ExternalProvider
178
- {
179
- AuthenticationScheme = x . Scheme ,
180
- DisplayName = x . DisplayName
181
- } ) ;
182
- providers . AddRange ( dyanmicSchemes ) ;
191
+ (
192
+ authenticationScheme : x . Scheme ,
193
+ displayName : x . DisplayName ?? x . Scheme
194
+ ) ) ;
195
+ providers . AddRange ( dynamicSchemes ) ;
183
196
184
197
185
198
var allowLocal = true ;
186
199
var client = context ? . Client ;
187
200
if ( client != null )
188
201
{
189
202
allowLocal = client . EnableLocalLogin ;
190
- if ( client . IdentityProviderRestrictions != null && client . IdentityProviderRestrictions . Any ( ) )
203
+ if ( client . IdentityProviderRestrictions != null && client . IdentityProviderRestrictions . Count != 0 )
191
204
{
192
205
providers = providers . Where ( provider => client . IdentityProviderRestrictions . Contains ( provider . AuthenticationScheme ) ) . ToList ( ) ;
193
206
}
@@ -200,4 +213,4 @@ private async Task BuildModelAsync(string returnUrl)
200
213
ExternalProviders = providers . ToArray ( )
201
214
} ;
202
215
}
203
- }
216
+ }
0 commit comments