We have a SPA with angularjs 1.6 and asp.net web API. We use Microsoft Account Authentication in OWIN middleware.
In Startup.Auth.cs
MicrosoftAccountAuthenticationOptions microsoftAccountAuthenticationOptions = new MicrosoftAccountAuthenticationOptions()
{
Caption = "Connection with your Microsoft account",
ClientId = microsoftAccountAuthenticationConfigurationElement.ClientId,
ClientSecret = microsoftAccountAuthenticationConfigurationElement.ClientSecret,
};
app.UseMicrosoftAccountAuthentication(microsoftAccountAuthenticationOptions);
Our controller method to challenge the authentication
[Route("ExternalLogin")]
[HttpPost]
[AllowAnonymous]
public IHttpActionResult ExternalLogin(string authenticationType, string returnUrl)
{
return new ChallengeResult(new List<AuthenticationHeaderValue>(), this)
{
RedirectUri = this.Url.Route("ExternalLoginCallback", new { returnUrl }),
AuthenticationType = authenticationType
};
}
And the ChallengeResult class
internal class ChallengeResult : UnauthorizedResult
{
public ChallengeResult(IEnumerable<AuthenticationHeaderValue> challenges, ApiController controller):base(challenges, controller)
{ }
public string AuthenticationType { get; set; }
public string RedirectUri { get; set; }
public override Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
HttpContext.Current.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = this.RedirectUri }, this.AuthenticationType);
return base.ExecuteAsync(cancellationToken);
}
}
This works perfectly.
Now we want add an Office365 authentication. So we use Windows Azure Active Directory authentication:
WindowsAzureActiveDirectoryBearerAuthenticationOptions windowsAzureActiveDirectoryBearerAuthenticationOptions = new WindowsAzureActiveDirectoryBearerAuthenticationOptions()
{
Tenant = windowsAzureActiveDirectoryAuthenticationConfigurationElement.Tenant,
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = windowsAzureActiveDirectoryAuthenticationConfigurationElement.Audience
}
};
app.UseWindowsAzureActiveDirectoryBearerAuthentication(windowsAzureActiveDirectoryBearerAuthenticationOptions);
And we reuse ExternalLogin api to challenge the authentication.
In this case the client receive a unauthorized result 401 instead of the redirect 302.
My question : Why the authentication challenge doesn't tranform the unauthorized result in redirect result on https://login.microsoftonline.com/ ?
Note : If use ADAl.js on client side to challenge the authentication then the redirection works. But I don't wish use the adal.js library
In the case of a SPA + API, the API should do Bearer token authentication as you have configured now.
But the authentication redirect should start from the SPA using ADAL.JS etc.
An API should not do 302 redirects for authentication.
If the caller is a program running on a server, what are they supposed to do?
That's why it returns a 401, because authentication failed.
Related
In my AccountController I have the following methods:
/*
* Called when requesting to sign up or sign in
*/
public void SignUpSignIn(string redirectUrl)
{
redirectUrl = redirectUrl ?? "/";
// Use the default policy to process the sign up / sign in flow
HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = redirectUrl });
return;
}
/*
* Called when requesting to sign up
*/
public void SignUp()
{
// Use the default policy to process the sign up flow
HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/" }, Globals.SignUpPolicyId);
return;
}
The UserFlow is set up inside of Azure, called B2C_1_signup, and that's what Globals.SignUpPolicyId evaluates to. Yet, whenever I test it out, I get an HTTP 401 error.
Here's the razor code that creates my button/link:
#Html.ActionLink("Sign Up!", "SignUp", "Account", routeValues: null, htmlAttributes: new { id = "signUpLink", #class = "btn btn-default" })
Whenever I test the link provided by Microsoft inside of the B2C Tenant, it brings up the Sign Up page correctly.
Here's the cleansed link provided by Microsoft for testing:
https://mytenantname.b2clogin.com/mytenantname.onmicrosoft.com/oauth2/v2.0/authorize?p=B2C_1_signup&client_id=RANDOM_GUID&nonce=defaultNonce&redirect_uri=http%3A%2F%2Flocalhost%3A1111&scope=openid&response_type=id_token&prompt=login
What am I missing??
• The redirect URI string defined in the account controller should be defined in the app config settings as a private static string and the B2C policies as different identifiers as public static strings due to which when during the user flow, authentication redirection will happen through by referencing the concerned app config string rather than finding it in the controller file itself. Since, you are encountering HTTP 401 error due to authentication issues related to the browser session.
Please find below the app controller sample methods calling the Azure AD B2C policies which works correctly as defined below for sign up, sign in and profile of
the user to be authenticated: -
public class AccountController : Controller
{
public void SignIn()
{
if (!Request.IsAuthenticated)
{
// To execute a policy, you simply need to trigger an OWIN challenge.
// You can indicate which policy to use by specifying the policy id as the AuthenticationType
HttpContext.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties() { RedirectUri = "/" }, Startup.SignInPolicyId);
}
}
public void SignUp()
{
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties() { RedirectUri = "/" }, Startup.SignUpPolicyId);
}
}
public void Profile()
{
if (Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties() { RedirectUri = "/" }, Startup.ProfilePolicyId);
}
}
public void SignOut()
{
// To sign out the user, you should issue an OpenIDConnect sign out request
if (Request.IsAuthenticated)
{
IEnumerable<AuthenticationDescription> authTypes = HttpContext.GetOwinContext().Authentication.GetAuthenticationTypes();
HttpContext.GetOwinContext().Authentication.SignOut(authTypes.Select(t => t.AuthenticationType).ToArray());
}
}
}
Also, refer the below link for more clarified information: -
https://bitoftech.net/2016/08/31/integrate-azure-ad-b2c-asp-net-mvc-web-app/
Also, find the below gif output for reference: -
I'm using Azure AD Connection for integrated login in an ASP.Net website.
If I use the same code and same configurations in a brand new project, it works. When implementing in my main project, it has some unusual behaviour.
Startup.cs is the following:
public class startup
{
string clientId = System.Configuration.ConfigurationManager.AppSettings["ClientId"];
string redirectUri = System.Configuration.ConfigurationManager.AppSettings["RedirectUri"];
static string tenant = System.Configuration.ConfigurationManager.AppSettings["Tenant"];
string authority = String.Format(System.Globalization.CultureInfo.InvariantCulture, System.Configuration.ConfigurationManager.AppSettings["Authority"], tenant);
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
Scope = OpenIdConnectScope.OpenIdProfile,
ResponseType = OpenIdConnectResponseType.IdToken,
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = false
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed
}
}
);
}
private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
{
context.HandleResponse();
context.Response.Redirect("/?errormessage=" + context.Exception.Message);
return Task.FromResult(0);
}
}
My c# code in login page:
protected void btnLogin_Click(object sender, EventArgs e)
{
SignIn();
}
public void SignIn()
{
if (!Request.IsAuthenticated)
{
HttpContext.Current.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties { RedirectUri = "/MainAzure.aspx" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
else
Response.Redirect("MainAzure.aspx");
}
The issue is this:
When I Press the login button in my aspx page, it opens the Microsoft login page. When I select the account and enter the password, it redirects to my redirect page (MainAzure.aspx) but without the authentication properties. Request.IsAuthenticated returns false.
Any hints?
Try changing your implementation as below approach:
Startup.cs :
public class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
/*
* Configure the OWIN middleware
*/
public void ConfigureAuth(IAppBuilder app)
{
// Required for Azure webapps, as by default they force TLS 1.2 and this project attempts 1.0
..ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
// Generate the metadata address using the tenant and policy information
MetadataAddress = String.Format(Globals.WellKnownMetadata, Globals.Tenant, Globals.DefaultPolicy),
// These are standard OpenID Connect parameters, with values pulled from web.config
ClientId = Globals.ClientId,
RedirectUri = Globals.RedirectUri,
PostLogoutRedirectUri = Globals.RedirectUri,
// Specify the callbacks for each type of notifications
Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = OnRedirectToIdentityProvider,
AuthorizationCodeReceived = OnAuthorizationCodeReceived,
AuthenticationFailed = OnAuthenticationFailed,
},
// Specify the claim type that specifies the Name property.
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
ValidateIssuer = false
},
// Specify the scope by appending all of the scopes requested into one string (separated by a blank space)
Scope = $"openid profile offline_access {Globals.ReadTasksScope} {Globals.WriteTasksScope}"
}
);
}
/*
* On each call to Azure AD B2C, check if a policy (e.g. the profile edit or password reset policy) has been specified in the OWIN context.
* If so, use that policy when making the call. Also, don't request a code (since it won't be needed).
*/
private Task OnRedirectToIdentityProvider(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
var policy = notification.OwinContext.Get<string>("Policy");
if (!string.IsNullOrEmpty(policy) && !policy.Equals(Globals.DefaultPolicy))
{
notification.ProtocolMessage.Scope = OpenIdConnectScope.OpenId;
notification.ProtocolMessage.ResponseType = OpenIdConnectResponseType.IdToken;
notification.ProtocolMessage.IssuerAddress = notification.ProtocolMessage.IssuerAddress.ToLower().Replace(Globals.DefaultPolicy.ToLower(), policy.ToLower());
}
return Task.FromResult(0);
}
/*
* Catch any failures received by the authentication middleware and handle appropriately
*/
private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
notification.HandleResponse();
// Handle the error code that Azure AD B2C throws when trying to reset a password from the login page
// because password reset is not supported by a "sign-up or sign-in policy"
if (notification.ProtocolMessage.ErrorDescription != null && notification.ProtocolMessage.ErrorDescription.Contains("AADB2C90118"))
{
// If the user clicked the reset password link, redirect to the reset password route
notification.Response.Redirect("/Account/ResetPassword");
}
else if (notification.Exception.Message == "access_denied")
{
notification.Response.Redirect("/");
}
else
{
notification.Response.Redirect("/Home/Error?message=" + notification.Exception.Message);
}
return Task.FromResult(0);
}
/*
* Callback function when an authorization code is received
*/
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification notification)
{
try
{
/*
The `MSALPerUserMemoryTokenCache` is created and hooked in the `UserTokenCache` used by `IConfidentialClientApplication`.
At this point, if you inspect `ClaimsPrinciple.Current` you will notice that the Identity is still unauthenticated and it has no claims,
but `MSALPerUserMemoryTokenCache` needs the claims to work properly. Because of this sync problem, we are using the constructor that
receives `ClaimsPrincipal` as argument and we are getting the claims from the object `AuthorizationCodeReceivedNotification context`.
This object contains the property `AuthenticationTicket.Identity`, which is a `ClaimsIdentity`, created from the token received from
Azure AD and has a full set of claims.
*/
IConfidentialClientApplication confidentialClient = MsalAppBuilder.BuildConfidentialClientApplication(new ClaimsPrincipal(notification.AuthenticationTicket.Identity));
// Upon successful sign in, get & cache a token using MSAL
AuthenticationResult result = await confidentialClient.AcquireTokenByAuthorizationCode(Globals.Scopes, notification.Code).ExecuteAsync();
}
catch (Exception ex)
{
throw new HttpResponseException(new HttpResponseMessage
{
StatusCode = HttpStatusCode.BadRequest,
ReasonPhrase = $"Unable to get authorization code {ex.Message}."
});
}
}
(For user authentication activities) AccountController.cs :
public class AccountController : Controller
{
/*
* Called when requesting to sign up or sign in
*/
public void SignUpSignIn(string redirectUrl)
{
redirectUrl = redirectUrl ?? "/";
// Use the default policy to process the sign up / sign in flow
HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = redirectUrl });
return;
}
/*
* Called when requesting to edit a profile
*/
public void EditProfile()
{
if (Request.IsAuthenticated)
{
// Let the middleware know you are trying to use the edit profile policy (see OnRedirectToIdentityProvider in Startup.Auth.cs)
HttpContext.GetOwinContext().Set("Policy", Globals.EditProfilePolicyId);
// Set the page to redirect to after editing the profile
var authenticationProperties = new AuthenticationProperties { RedirectUri = "/" };
HttpContext.GetOwinContext().Authentication.Challenge(authenticationProperties);
return;
}
Response.Redirect("/");
}
/*
* Called when requesting to reset a password
*/
public void ResetPassword()
{
// Let the middleware know you are trying to use the reset password policy (see OnRedirectToIdentityProvider in Startup.Auth.cs)
HttpContext.GetOwinContext().Set("Policy", Globals.ResetPasswordPolicyId);
// Set the page to redirect to after changing passwords
var authenticationProperties = new AuthenticationProperties { RedirectUri = "/" };
HttpContext.GetOwinContext().Authentication.Challenge(authenticationProperties);
return;
}
/*
* Called when requesting to sign out
*/
public async Task SignOut()
{
// To sign out the user, you should issue an OpenIDConnect sign out request.
if (Request.IsAuthenticated)
{
await MsalAppBuilder.ClearUserTokenCache();
IEnumerable<AuthenticationDescription> authTypes = HttpContext.GetOwinContext().Authentication.GetAuthenticationTypes();
HttpContext.GetOwinContext().Authentication.SignOut(authTypes.Select(t => t.AuthenticationType).ToArray());
Request.GetOwinContext().Authentication.GetAuthenticationTypes();
}
}
}
For complete code, you can visit Azure AD B2C: Call an ASP.NET Web API from an ASP.NET Web App
I have a Mobile App I am writing, at present it is simply the To Do Item list quick start application with custom Authentication added. I have the associated Xamarin Forms app.
From the App I am able to login using the LoginAsync method, my website returns a token and shows the username I am logging in as, but subsequent calls suggest I am not authorised.
After a bit of debugging, I can see that the request arrives at the web server with the X-ZUMO-AUTH header and the token in the value, but I can see that the User does not seem to be populated and the call to the GetAllTodoItems method is returned as 401:Unauthorized.
In the startup code for the website, the ConfigureMobileApp contains the following:
app.UseWebApi(config);
if (string.IsNullOrEmpty(settings.HostName))
{
// This middleware is intended to be used locally for debugging. By default, HostName will
// only have a value when running in an App Service application.
app.UseAppServiceAuthentication(new AppServiceAuthenticationOptions
{
SigningKey = ConfigurationManager.AppSettings["SigningKey"],
ValidAudiences = new[] { ConfigurationManager.AppSettings["ValidAudience"] },
ValidIssuers = new[] { ConfigurationManager.AppSettings["ValidIssuer"] },
TokenHandler = config.GetAppServiceTokenHandler()
});
}
I have an account controller class:
[Route(".auth/login/custom")]
public class AccountController : ApiController
{
private static string URL = "https://myapidev.azurewebsites.net/";
private static string KEY = "FC31EB8CAAAAAA9D74EEE3613A7A08CA65CB1ACAA8CEFF82A5B5E915625B31D";
public AccountController()
{
}
[HttpPost]
public IHttpActionResult Post([FromBody] LoginUser assertion)
{
if (isValidAssertion(assertion))
{
JwtSecurityToken token = AppServiceLoginHandler.CreateToken(new Claim[] { new Claim(JwtRegisteredClaimNames.Sub, assertion.username) },
ConfigurationManager.AppSettings["SigningKey"],
ConfigurationManager.AppSettings["ValidAudience"],
ConfigurationManager.AppSettings["ValidIssuer"],
TimeSpan.FromHours(24));
return Ok(new LoginResult()
{
authenticationToken = token.RawData,
user = new LoginResultUser() { userId = assertion.username }
});
}
else // user assertion was not valid
{
return ResponseMessage(Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "Invalid Request"));
}
}
private bool isValidAssertion(LoginUser assertion)
{
return assertion != null;
}
}
The TodoItemController contains the following:
[Authorize]
[MobileAppController]
public class TodoItemController : TableController<TodoItem>
{
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
EducaterAPIDevContext context = new EducaterAPIDevContext();
DomainManager = new EntityDomainManager<TodoItem>(context, Request);
//// Get the SID of the current user.
//var claimsPrincipal = this.User as ClaimsPrincipal;
//string sid = claimsPrincipal.FindFirst(ClaimTypes.NameIdentifier).Value;
}
// GET tables/TodoItem
public IQueryable<TodoItem> GetAllTodoItems()
{
return Query();
}
...
...
}
On calling the query method from the Xamarin App, it returns with 401 even though the X-ZUMO-AUTH is in the headers and contains the correct token issued by the login method.
Have I missed something or has anyone come across this issue before - any help would be appreciated?
Have you turned on Authentication/Authorization in your App Service? Without it, the token will never be decoded.
This is the most common issue.
After digging I found the issue, initially there was a configuration issue - the above comments helped thanks. The Audiences and Issuers must match your azure site including including the trailing slash.
The issue once the configuration had been corrected was that the token which is passed correctly from my App did not get processed at the server side so all Authorized areas where out-of-bounds. This was because of the order of calls in the ConfigureMobileApp method. I was calling the app.UseWebApi method before the app.UseAppServiceAuthentication method, changing the order suddenly had the token being tested again.
The dummy site I have working now has the following:
public static void ConfigureMobileApp(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
//For more information on Web API tracing, see http://go.microsoft.com/fwlink/?LinkId=620686
SystemDiagnosticsTraceWriter traceWriter = config.EnableSystemDiagnosticsTracing();
new MobileAppConfiguration()
.UseDefaultConfiguration()
.MapApiControllers()
.ApplyTo(config);
config.MapHttpAttributeRoutes();
// Use Entity Framework Code First to create database tables based on your DbContext
//Database.SetInitializer(new EducaterAPIDevInitializer());
// To prevent Entity Framework from modifying your database schema, use a null database initializer
// Database.SetInitializer<EducaterAPIDevContext>(null);
MobileAppSettingsDictionary settings = config.GetMobileAppSettingsProvider().GetMobileAppSettings();
if (string.IsNullOrEmpty(settings.HostName))
{
var options = new AppServiceAuthenticationOptions
{
SigningKey = ConfigurationManager.AppSettings["SigningKey"],
ValidAudiences = new[] { ConfigurationManager.AppSettings["ValidAudience"] },
ValidIssuers = new[] { ConfigurationManager.AppSettings["ValidIssuer"] },
TokenHandler = config.GetAppServiceTokenHandler()
};
app.UseAppServiceAuthentication(options);
}
app.UseWebApi(config);
}
I setup my own oauth authorization Server (NO Login via Facebook, Google and co). Unfortunately my server does not redirect to the login page after the Authorize-Endpoint is hit. Why doesn't the middleware redirect to my login page?
I followed some tutorials:
OWIN OAuth 2.0 Authorization Server
OAuth custom provider c# (Which is by the way the only code example i found for client login into custom oauth server. Thanks to #MatthiasRamp. All other stuff I read is about how to login with the social media Clients which is very frustrating)
MVC 5 application - implement OAuth Authorization code flow
This is what I made shortend on my oauth server side:
Startup.Auth.cs use active Cookie Authentication with Loginpath
Startup.Auth.cs use passive Cookie Authentication
Startup.Auth.cs use authorization Server with AuthorizeEndpointPath
SecurityController/Authorize calls Authentication.challenge() to change the status to 401
One point in the post from #Satish P he described:
Redirect the client to a login page
For this purpose I set the property CookieAuthenticationOptions.LoginPath which tells me
The LoginPath property informs the middleware that it should change an outgoing 401 Unauthorized status code into a 302 redirection onto the given login path.
This is my Startup.Auth.cs including LoginPath:
public void ConfigureAuth(IAppBuilder app)
{
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Application",
AuthenticationMode = AuthenticationMode.Passive,
LoginPath = new PathString("/Security/Login"),
LogoutPath = new PathString("/Security/Logout")
});
// Enable the External Sign In Cookie.
app.SetDefaultSignInAsAuthenticationType("External");
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "External",
AuthenticationMode = AuthenticationMode.Passive,
CookieName = CookieAuthenticationDefaults.CookiePrefix + "External",
ExpireTimeSpan = TimeSpan.FromMinutes(5),
});
// The UseOAuthAuthorizationServer extension method is to setup the authorization server. The setup options are: [...]
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
{
AuthorizeEndpointPath = new PathString("/Security/Authorize"),
TokenEndpointPath = new PathString("/Token"),
ApplicationCanDisplayErrors = true,
#if DEBUG
AllowInsecureHttp = true,
#endif
// Authorization server provider which controls the lifecycle of Authorization Server
Provider = new ApplicationOAuthProvider(PublicClientId)
});
}
I created a class which derives from OAuthAuthorizationServerProvider.
public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
// [Check username and pw here]
var oAuthIdentity = new ClaimsIdentity(new GenericIdentity(context.UserName, OAuthDefaults.AuthenticationType), context.Scope.Select(x => new Claim("urn:oauth:scope", x)));
var cookiesIdentity = new ClaimsIdentity(new GenericIdentity(context.UserName, CookieAuthenticationDefaults.AuthenticationType), context.Scope.Select(x => new Claim("urn:oauth:scope", x)));
AuthenticationProperties properties = CreateProperties(context.UserName);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return Task.FromResult<object>(null);
}
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
var grantType = context.Parameters.SingleOrDefault(p => p.Key == "grant_type").Value;
if (grantType != null)
{
if (grantType[0] == "authorization_code")
{
string clientId;
string clientSecret;
if (context.TryGetBasicCredentials(out clientId, out clientSecret) || context.TryGetFormCredentials(out clientId, out clientSecret))
{
if (clientId == Clients.ClientApp.Id && clientSecret == Clients.ClientApp.Secret)
{
context.Validated();
}
}
}
}
return Task.FromResult<object>(null);
}
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
if (context.ClientId == Clients.ClientApp.Id)
{
context.Validated(Clients.ClientApp.RedirectUrl);
}
return Task.FromResult<object>(null);
}
public static AuthenticationProperties CreateProperties(string userName)
{
IDictionary<string, string> data = new Dictionary<string, string>
{
{ "userName", userName }
};
return new AuthenticationProperties(data);
}
}
And this is the where the redirect should happen because challenge will change the response to unauthorized (401). Instead of the redirect to the login page it wants to return the authorize view.
public ActionResult Authorize()
{
if (Response.StatusCode != 200)
{
return View("AuthorizeError");
}
var authType = DefaultAuthenticationTypes.ApplicationCookie;
var authentication = HttpContext.GetOwinContext().Authentication;
var ticket = authentication.AuthenticateAsync(authType).Result;
var identity = ticket != null ? ticket.Identity : null;
if (identity == null)
{
authentication.Challenge(authType);
}
else
{
// login stuff
}
return View();
}
I know it's too late and you might not need this anymore but just in case if anyone else also faces this issue then you need to return HttpUnauthorizedResult(); after challenge and it will rediret you to login page.
e.g
if (identity == null)
{
authentication.Challenge(authType);
return new HttpUnauthorizedResult();
}
//login stuff
I have Global Filter as
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new AuthorizeAttribute());
}
My Signin Code is
[AllowAnonymous,HttpPost]
public async Task<ActionResult> Login(UsersViewModel usermodel)
{
if (ModelState.IsValid)
{
Mapper.CreateMap<UsersViewModel, Users>();
Users model = Mapper.Map<Users>(usermodel);
Users result = await UserManager.FindAsync(model.UserName, usermodel.Password);
if (result != null)
{
await SignInAsync(result, true);
return RedirectToAction("Success", "Home");
}
ModelState.AddModelError("Error", "Incorrect username and/or password");
}
return RedirectToAction("Index", "Home");
}
private async Task SignInAsync(Users user, bool isPersistent)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
ClaimsIdentity identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ExternalCookie);
AuthenticationManager.SignIn(new AuthenticationProperties()
{
IsPersistent = isPersistent
}, identity);
}
Now when I am signing in then its redirecting to /Home/Success. Its alright I saw the cookies its also creating the authentication cookie. However when I am trying to access /Account/AddRole which isnt marked as allowannonymous. In spite I have already authenticated . Its still redirecting me to /Account/Login . Which shouldn't happen . However my Authentication Startup Class for Owin is :
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(() => new RemsContext());
app.CreatePerOwinContext<RemsContext>(() => new RemsContext());
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
CookieName = "Authcookie",
CookieSecure = CookieSecureOption.Always,
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
}
Why its not authentication working inspite it sets the authentication cookie for Asp.net ? From Identity Framework . I m currently using Identityframework beta 2.0.1 . can someone guide why authorize in global isnt working as expected in spite I have already logged in and the authentication cookie has been set ?