Web API Token Authentication throws exception No authentication handler is configured to handle the scheme: Microsoft.AspNet.Identity.Application - asp.net-mvc

I have implemented Token Authentication for Web API using ASP.Net Core by following the solution mentioned in following post
Token Based Authentication in ASP.NET Core
To implement the authentication logic, I have defined following method
public async Task<bool> AuthenticateUser(string email, string password)
{
UserManager<ApplicationUser> _userManager = HttpContext.ApplicationServices.GetService(typeof(UserManager<ApplicationUser>)) as UserManager<ApplicationUser>;
SignInManager<ApplicationUser> _signInManager = HttpContext.ApplicationServices.GetService(typeof(SignInManager<ApplicationUser>)) as SignInManager<ApplicationUser>;
var result = await _signInManager.PasswordSignInAsync(email, password, isPersistent: false, lockoutOnFailure: false);
if (result.Succeeded)
{
return true;
}
else
{
return false;
}
}
and the Post method with is invoked is
[HttpPost]
public dynamic Post([FromBody] AuthRequest req)
{
string email = req.username;
string password = req.password;
try
{
bool isAuthenticated = false;
//implement the authentication logic over here
isAuthenticated = AuthenticateUser(email, password).Result;
if (isAuthenticated)
{
DateTime? expires = DateTime.UtcNow.AddDays(2);
var token = GetToken(req.username, expires);
return new { authenticated = true, entityId = 1, token = token, tokenExpires = expires };
}
}
catch (Exception ex)
{
return new { authenticated = false, message = "Exception: " + ex.Message, detailedmessage = ex.InnerException};
}
return new { authenticated = false };
}
Now the problem...
The Post executes fine on first call and returns the desired result, however, on second call, it throws following exception
No authentication handler is configured to handle the scheme: Microsoft.AspNet.Identity.Application
On debugging I found that this exception is being thrown when following line is executed
var result = await _signInManager.PasswordSignInAsync(email, password, isPersistent: false, lockoutOnFailure: false);
It works fine when invoked for the first time but throws exception on all subsequent calls.
I've been searching for this issue for the past 2 days and all I find is that in Startup.cs app.UseIdentity(); should be invoked before adding the authentication middleware. It's already happeneing in my code.
Please suggest what am I missing here.

Resolved the issue by changing HttpContext.ApplicationServices.GetService() to HttpContext.RequestServices.GetService() in AuthenticateUser() method. My updated method is
public async Task<bool> AuthenticateUser(string email, string password)
{
UserManager<ApplicationUser> _userManager = HttpContext.RequestServices.GetService(typeof(UserManager<ApplicationUser>)) as UserManager<ApplicationUser>;
SignInManager<ApplicationUser> _signInManager = HttpContext.RequestServices.GetService(typeof(SignInManager<ApplicationUser>)) as SignInManager<ApplicationUser>;
var result = await _signInManager.PasswordSignInAsync(email, password, isPersistent: false, lockoutOnFailure: false);
if (result.Succeeded)
{
return true;
}
else
{
return false;
}
}

Related

How to access session in OnAuthorizationCodeReceived

I am not sure ,but is it possible to access session variable in OnAuthorizationCodeReceived callback while getting access token from.
My requirement is like using DBConnection ,to store token in database . As Session["DBConnection "] is always null.
Using ASP.NET MVC 5.
Thanks
public void SignIn()
{
Session["DBConnection "] = GetConnectionObject();
// Send an OpenID Connect sign-in request.
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
In Startup.Auth.cs
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification context)
{
// Upon successful sign in, get the access token & cache it using MSAL
IConfidentialClientApplication clientApp = await MsalAppBuilder.BuildConfidentialClientApplication();
AuthenticationResult authResult = await clientApp.AcquireTokenByAuthorizationCode(new[] { "User.Read" }, context.Code).ExecuteAsync();
string token = authResult?.IdToken;
if (token != null)
{
/* connection is always null*/
var connection= HttpContext.Current.Session["DBConnection "]
context.HandleResponse();
context.Response.Redirect(context.RedirectUri);
}
}

O365 login is not working after log out in MVC

There is some problem in my code not sure where I am getting wrong. Earlier same code used to work properly. Now I am trying to log in to application in both the ways i.e through ClaimPrincipal and Claim Identity. In both the ways Sometime data is null. Not sure where is the issue.
Below is my code
StartUp.cs
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = appId,
Authority = authority,
RedirectUri = redirectUri,
//PostLogoutRedirectUri = redirectUri,
Scope = OpenIdConnectScope.OpenIdProfile,
ResponseType = OpenIdConnectResponseType.IdToken,
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = false // This is a simplification
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed
}
}
);
SignIn Method
[AllowAnonymous]
public void SignIn(string ReturnUrl = "/", string loginType = "")
{
HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/Account/Office365LoginCallback" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
RedirectURL Code
[AllowAnonymous]
[ExceptionHandler]
public async Task<ActionResult> Office365LoginCallback(string code)
{
var userClaims = User.Identity as System.Security.Claims.ClaimsIdentity;
string userName = userClaims?.FindFirst("name")?.Value;
string userEmail = userClaims?.FindFirst("preferred_username")?.Value;
string userId = userClaims?.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
ViewBag.TenantId = userClaims?.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid")?.Value;
return Redirect("~/");
}
catch (Exception ex)
{
throw ex;
}
}
SignOut Method
public ActionResult LogOff()
{
//AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
//HttpContext.GetOwinContext().Authentication.SignOut();
FormsAuthentication.SignOut();
//HttpContext.GetOwinContext().Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType);
//HttpContext.Session[AppConstants.UserEmail] = null;
//HttpContext.Session[AppConstants.UserUpload] = null;
//HttpContext.Session[AppConstants.UserImage] = null;
//HttpContext.Session[AppConstants.CurrentRole] = null;
//HttpContext.Session[AppConstants.Users] = null;
//Session.Clear();
//Session.Abandon();
return RedirectToAction("Login", "Account");
}
Any Help will be appreciated. FYI I have tried same code in new project there is working fine but here in my old application its not working once clicked on Logout

Not receiving access_token in three-legged oauth 2.0 flow in asp.net mvc (Blackboard Learn)

I have to implement three-legged authentication in ASP.NET MVC. I have followed the steps according to the Blackboard documentation, especially the link https://community.blackboard.com/docs/DOC-3976-three-legged-oauth
I have received authorization code by calling the REST API /learn/api/public/v1/oauth2/authorizationcode.After that according to the documentation (I followed the documentation exactly but I don't know what am I have been missing ), I built a POST request to /learn/api/public/v1/oauth2/token to get access_token but I am unable to get access_token.
Instead, access_token, I have been receiving a BadRequest. This means I am making a mistake to build my second request but I am unable to fix the problem. I haven't found any code sample in .NET to implement three legged authentication for Blackboard Learn. Could you please help me to resolve the issue?
This is my code to call both APIs to receive access_token.
public class HomeController : Controller
{
public ActionResult Index()
{
// GET /learn/api/public/v1/oauth2/authorizationcode
Guid stateId = Guid.NewGuid();
string applicationKey = "Application key goes here";
string redirectUrl = string.Format("https://Blackboard Learn URL goes here/learn/api/public/v1/oauth2/authorizationcode" +
"?redirect_uri=https://localhost:44300/Home/OAuth2Response&response_type=code&client_id={0}&scope=read&state={1}",
applicationKey, stateId);
Response.Redirect(redirectUrl, true);
return View();
}
public async Task<bool> OAuth2Response(string code = null, string state = null, string error = null, string error_description = null)
{
bool success = true;
string json = string.Empty;
string urlCommand = string.Format("/learn/api/public/v1/oauth2/token?code={0}&redirect_url=https://localhost:44300/Home/OAuth2Response", code);
try
{
using (HttpClient client = new HttpClient())
{
var endpoint = new Uri("Blackboard Learn URL goes here" + urlCommand);
var postData = new List<KeyValuePair<string, string>>();
postData.Add(new KeyValuePair<string, string>("grant_type", "authorization_code"));
HttpContent body = new FormUrlEncodedContent(postData);
// POST /learn/api/public/v1/oauth2/token
using (HttpResponseMessage response = await client.PostAsync(endpoint, body)) // Problem is here
{
if (response.IsSuccessStatusCode)
{
json = await response.Content.ReadAsStringAsync();
}
else
{
success = false;
}
}
}
}
catch (Exception err)
{
//hopefully we never end up here, log this exception for forensics
success = false;
}
return success;
}
}
NOTE: I can successfully receive an access_token in Postman tool.
Finally, the below code works perfectly for 3 legged authentications in ASP.NET MVC.
public class HomeController : Controller
{
//https://blackboard.jiveon.com/docs/DOC-3976-three-legged-oauth
public ActionResult Index()
{
// GET /learn/api/public/v1/oauth2/authorizationcode
Guid stateId = Guid.NewGuid();
string applicationKey = "Application key goes here";
string redirectUrl = string.Format("Blackboard Learn URL goes here/learn/api/public/v1/oauth2/authorizationcode" +
"?redirect_uri=https://localhost:44300/Home/OAuth2Response&response_type=code&client_id={0}&scope=read&state={1}",
applicationKey, stateId);
Response.Redirect(redirectUrl, true);
return View();
}
public async Task<bool> OAuth2Response(string code = null, string state = null, string error = null, string error_description = null)
{
bool success = true;
string json = string.Empty;
string urlCommand = string.Format("/learn/api/public/v1/oauth2/token?code={0}&redirect_uri=https://localhost:44300/Home/OAuth2Response", code);
try
{
using (HttpClient client = new HttpClient())
{
var endpoint = new Uri("Blackboard Learn URL goes here" + urlCommand);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes("client_id:client_secret")));
var postData = new List<KeyValuePair<string, string>>();
postData.Add(new KeyValuePair<string, string>("grant_type", "authorization_code"));
HttpContent body = new FormUrlEncodedContent(postData);
using (HttpResponseMessage response = await client.PostAsync(endpoint, body))
{
if (response.IsSuccessStatusCode)
{
json = await response.Content.ReadAsStringAsync();
dynamic oauth2Result = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
string access_token = oauth2Result.access_token;
string refresh_token = oauth2Result.refresh_token; }
else
{
success = false;
}
}
}
}
catch (Exception err) {
//hopefully we never end up here, log this exception for forensics
success = false;
}
return success;
}
}

WepApi TaskCanceledException A task was canceled. httpClient

It was working good, but I made some changes in the api, adding more controllers nothing out of the place, and then it stops working, always thrown an exception: "TaskCanceledException: A task was canceled" in the line GetAsync().result. I increase the timeout and infinitely stays loading.
The code controller APP who make a request to the controller API:
public ActionResult Login(LoginM us)
{
try
{
cuentaM account = new cuentaM();
HttpClient client = new HttpClient();
var result = client.GetAsync("http://localhost:26723/api/Login" + "?email=" + us.email + "&password=" + us.password).Result;
if (result.IsSuccessStatusCode)
{
account = result.Content.ReadAsAsync<cuentaM>().Result;
}
Session["cuenta"] = account;
return RedirectToAction("Index", "Home");
}
catch (Exception ex)
{
throw;
}
}
The controller API code:
public HttpResponseMessage Get(string email, string password)
{
try
{
using (elevationbEntities db = new elevationbEntities())
{
usuario user = db.usuarios.Where(m => m.email == email && m.password == password).SingleOrDefault();
cuentaM account = new cuentaM();
if (user != null)
{
account = (from o in db.cuentas
join cu in db.cuentausuarios on o.idCuenta equals cu.idCuenta
join u in db.usuarios on cu.idUsuario equals u.idUsuario
where u.idUsuario == user.idUsuario
select new cuentaM { idUsuario = user.idUsuario, idCuenta = o.idCuenta, CodigoUnico = o.CodigoUnico })
.FirstOrDefault();
}
else
{
account.Error = "Wrong Password or Email";
}
HttpResponseMessage response;
response = Request.CreateResponse(HttpStatusCode.OK, account);
return response;
}
}
catch (TaskCanceledException ex)
{
HttpResponseMessage response;
response = Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex);
return response;
}
}
Making blocking calls (.Result) on HttpClinet's async API can cause deadlocks especially if being used asp.net MVC, which may have async operations being invoked when the blocking call was made.
Make the code async all the way through.
Also try to avoid creating an instance of HttpClient on every request. This can cause sockets to be exhausted.
private static HttpClient client = new HttpClient();
public async Task<ActionResult> Login(LoginM us) {
try {
cuentaM account = new cuentaM();
var url = "http://localhost:26723/api/Login" + "?email=" + us.email + "&password=" + us.password
var result = await client.GetAsync(url);
if (result.IsSuccessStatusCode) {
account = await result.Content.ReadAsAsync<cuentaM>();
}
Session["cuenta"] = account;
return RedirectToAction("Index", "Home");
} catch (Exception ex) {
throw;
}
}
You may be deadlocking by blocking on an async call, as described in this article. Here's the problematic line:
account = result.Content.ReadAsAsync<cuentaM>().Result;
Change the method signature for Login to:
public async Task<ActionResult> Login(LoginM us)
Then change the problematic line to use await instead of .Result:
account = await result.Content.ReadAsAsync<cuentaM>();

Why does `UserManager.ConfirmEmailAsync` throw for an invalid user?

Given my code in ConfirmEmail:
var result = await UserManager.ConfirmEmailAsync(userId, code);
if (result.Succeeded)
{
model.Message = "Thank you for confirming your email.";
model.IsConfirmed = true;
return View(model);
}
based closely on the code from the standard MVC 5 project template, I would expect an invalid user to cause result.Succeeded == false, not to have ConfirmEmailAsync throw an InvalidOperationException.
The source code of UserManager.ConfirmEmailAsync is:
public virtual async Task<IdentityResult> ConfirmEmailAsync(TKey userId, string token)
{
ThrowIfDisposed();
var store = GetEmailStore();
var user = await FindByIdAsync(userId).ConfigureAwait(false);
if (user == null)
{
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Resources.UserIdNotFound, userId));
}
if (!await VerifyUserTokenAsync(userId, "Confirmation", token))
{
return IdentityResult.Failed(Resources.InvalidToken);
}
await store.SetEmailConfirmedAsync(user, true).ConfigureAwait(false);
return await UpdateAsync(user).ConfigureAwait(false);
}
You can see that InvalidOperationException is thrown when user was not found using FindByIdAsync(userId).
So this behaviour is by design.

Resources