I'm currently tasked with implementing mfa for our user authentications. I have everything set up where the client can query the Authorize endpoint and if they are required to enter mfa they are redirected to the endpoint to enter the code such and such.
My problem is with calling the Authorize Endpoint with username and passwords, I do not want to put passwords in a direct query to the server from the browser, so instead I want to use http post but that causes a problem where I am never returned the auth code.
As it stands the only way that I could figure out to get redirects to work from my Angular front end was to do a hack like so:
const path = `https://localhost:7123/Authorize?grant_type=password&username=${encodeURIComponent('Shane')}&password=${encodeURIComponent('Password')}` +
`&response_type=code&client_id=AuthServerPrac.Public&redirect_uri=${window.location.origin + window.location.pathname}`;
window.location.href = path;
However I do not want to put passwords inside a query params as that comes with problems.
So I tried to do a http post using the HttpClient which is problematic for redirects but I believe I can get around it, but then another problems appears where say the user does not need to mfa and a SignIn result is done then the auth code should be returned back to the client, but that uses a Query Response mode not JSON so my Post request never receives the code.
const data = `grant_type=password&username=${encodeURIComponent('Shane')}&password=${encodeURIComponent('Password')}` +
`&response_type=code&client_id=AuthServerPrac.Public&redirect_uri=${window.location.origin + window.location.pathname}`;
// cant have this it is not good to put password in query params
//window.location.href = path;
const headers = new HttpHeaders({'Content-Type': 'application/x-www-form-urlencoded'});
this.httpClient.post<any>(AppComponent.authApi + 'Authorize', data, {headers: headers}).subscribe({
next: (response) => {
console.log(response) // never get here
},
error: (e: HttpErrorResponse) => {
if (e.status === 302) {
console.log("must redirect to mfa redirect")
}
},
complete: () => console.log('got a complete notification'),
})
Here is my current implementation of Authorize Endpoint
[HttpGet("~/authorize")]
[HttpPost("~/authorize")]
public async Task<IActionResult> Authorize()
{
var request = HttpContext.GetOpenIddictServerRequest();
if (request == null)
{
return Forbid(OIErrors.InvalidRequestObject,
"The OpenIddict server request cannot be retrieved");
}
// Resolve the claims stored in the cookie created after the Microsoft authentication dance.
// If the principal cannot be found, trigger a new challenge to redirect the user to Microsoft.
var result = await HttpContext.AuthenticateAsync(COOKIE_AUTH_SCHEME);
var principal = result.Principal;
if (principal == null)
{
if (request.IsPasswordGrantType())
{
// Password login
if (request.Username != "Shane" || request.Password != "Password")
{
return Forbid(OIErrors.InsufficientAccess, "You're not Shane!");
}
var isMfaRequired = false;
if (isMfaRequired)
{
var propertiesForMfa = new AuthenticationProperties(
new Dictionary<string, string>
{
{ "userId", 1.ToString() }
});
// Challenge will send the user to mfa page view
return Challenge(propertiesForMfa, MyMfaAuthenticationScheme.SCHEME_NAME);
}
else
{
const string USER_ID = "1";
var identity = new ClaimsIdentity(AUTH_SCHEME);
identity.AddClaim(OIClaims.Subject, USER_ID);
// Signing in with the OpenIddict authentication scheme trigger OpenIddict to issue a code (which can be exchanged for an access token)
return SignIn(new ClaimsPrincipal(identity), AUTH_SCHEME);
}
}
return Forbid(OIErrors.AccessDenied, "Grant Type not supported");
}
if (principal.Identity?.AuthenticationType == MyMfaAuthenticationScheme.AUTHENTICATION_TYPE)
{
return await ProcessMfa(result.Properties, principal.Identity as ClaimsIdentity);
}
return Forbid(OIErrors.AccessDenied, "AuthenticationType not supported");
}
So my question is how can I get the best of both worlds and safely http post the username and password to the Authorize Endpoint meanwhile being able to handle Redirects if mfa is requried. Thanks heaps!