O365 login is not working after log out in MVC - asp.net-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

Related

Azure AD Auto signing out after signin

So ive gone through as many posts as I can, and I cant seem to sort this out!
My client wants us to allow logging into their ADFS via an MVC platform that I have built, so I am trying to allow them to sign into their Azure AD to sign into the platform.
When I am redirected to my signin page for Azure AD(MS Login), I type in my credentials and then it looks like it is doing a quick redirect loop and then automatically signs me out, I am going crazy!!!
Below is everything I have setup:
On Azure AD:
Created App service and put ApplicationId and TenantId in my Web.config
<add key="ida:ClientId" value="ApplicationID from AzureAD" />
<add key="ida:Tenant" value="TenantId from AzureAD" />
<add key="ida:AADInstance" value="https://login.microsoftonline.com/{0}" />
<add key="ida:RedirectUri" value="https://sitename.azurewebsites.net/Home/Index" />
<add key="ida:PostLogoutRedirectUri" value="https://sitename.azurewebsites.net" />
On Startup.Auth.cs
public partial class Startup
{
// Calling the keys values from Web.config file
private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
private static string tenant = ConfigurationManager.AppSettings["ida:Tenant"];
private static string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
private static string postLogoutRedirectUri = ConfigurationManager.AppSettings["ida:PostLogoutRedirectUri"];
// Concatenate aadInstance, tenant to form authority value
private string authority = string.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
// ConfigureAuth method
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
//app.UseCookieAuthentication(new CookieAuthenticationOptions());
//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
//Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = (context) =>
{
context.HandleResponse();
context.OwinContext.Response.Redirect("/Home/Index");
return Task.FromResult(0);
}
}
});
} // end - ConfigureAuth method
On my routeConfig : This was done so that my custom landing page can be loaded first, on this page is a button saying "Enter platform", which the client will click on and go to Azure AD signin(MS Login page)
public static class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.LowercaseUrls = true;
routes.MapRoute("Default", "{controller}/{action}/{id}", new
{
controller = "Account",
action = "Login",
id = UrlParameter.Optional
}).RouteHandler = new DashRouteHandler();
}
}
Account Controller
[Authorize]
public void SignIn()
{
clsHomeScreen clsHomeScreen = new clsHomeScreen();
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/" }, OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
Response.Redirect("/");
}
[AllowAnonymous]
[OutputCache(NoStore = true, Location = OutputCacheLocation.None)]
public ActionResult Login(string returnUrl)
{
// We do not want to use any existing identity information
EnsureLoggedOut();
// Store the originating URL so we can attach it to a form field
var viewModel = new AccountLoginModel { ReturnUrl = returnUrl };
return View(viewModel);
}
HomeController - This is where it SHOULD be redirected to after signing in, but its not:
[Authorize]
public async Task<ActionResult> Index()
{
HomeScreenLists HS = new HomeScreenLists();
IEnumerable<Challenges> ActiveChallenges;
IEnumerable<Challenges> PrivateChallenges;
string loggedInUserId = "";
string loggedInEmail = "";
var userClaims = User.Identity as System.Security.Claims.ClaimsIdentity;
string email = userClaims?.FindFirst(System.IdentityModel.Claims.ClaimTypes.Name)?.Value;
string firstname = userClaims?.FindFirst(System.IdentityModel.Claims.ClaimTypes.GivenName)?.Value;
string lastname = userClaims?.FindFirst(System.IdentityModel.Claims.ClaimTypes.Surname)?.Value;
string userId = "";
//The Email will not contain an #(i.e. an email address) if not using Azure AD to sign in.
if (!email.Contains("#"))
{
loggedInUserId = User.Identity.GetUserId();
goto LoggedInUser_Found;
}
if (TempData["LoggedInEmail"] != null)
{
if (email != TempData["LoggedInEmail"].ToString())
{
userId = clsHomeScreen.GetUserId(TempData["LoggedInEmail"].ToString());
}
else
{
userId = clsHomeScreen.GetUserId(email);
}
}
if (email != null)
{
userId = clsHomeScreen.GetUserId(email);
}
if (userId == null || userId == "")
{
clsUsers clsUsers = new clsUsers();
if (TempData["LoggedInEmail"] != null)
{
loggedInEmail = TempData["LoggedInEmail"].ToString();
var userDetails = clsUsers.GetUsers().Where(x => x.Email == loggedInEmail).FirstOrDefault();
loggedInUserId = userDetails.Id;
}
else
{
if(userId == null)
{
await RegisterAAD();
userId = clsHomeScreen.GetUserId(email);
loggedInUserId = userId;
}
else
{
loggedInUserId = User.Identity.GetUserId();
}
}
}
else
{
loggedInUserId = userId;
}
LoggedInUser_Found:
int iBU = (int)db.Users.FirstOrDefault(x => x.Id == loggedInUserId).fkiBusinessUnitId;
if (iBU == 0)
{
HS.HasBU = false;
TempData["HasBU"] = "No";
TempData["UserId"] = loggedInUserId;
}
else
{
HS.HasBU = true;
TempData["HasBU"] = "Yes";
TempData["UserId"] = loggedInUserId;
}
bool isAdmin = false;
if (User.IsInRole("Administrator"))
{
isAdmin = true;
}
ActiveChallenges = clsChallenges.GetActiveChallenges();
PrivateChallenges = clsChallenges.GetPrivateChallenges(loggedInUserId, isAdmin);
HS.HomeScreenList = clsHomeScreen.GetHomeScreenAdverts();
HS.ActiveChallengesList = ActiveChallenges;
HS.PrivateChallengesList = PrivateChallenges;
HS.UserId = loggedInUserId;
return View(HS);
}
So if I remove the [Authorize] attribute on the Index ActionResult, then it does a continuous redirect loop.
What ive tried:
I have tried using the KentorCookiSaver, which didnt work.
Recreating the app service
Changed the redirectUrl in Azure AD App Registration
Someone even spoke about rewriting the cookies, which I tried, but dont know if I followed the steps correctly, the link is Here
Ive tried so many things that I cant even remember what ive tried. Could anyone possibly help with what I am doing wrong, please.
Thanks a million!
So with some help from someone who knows these things, my problem was solved.
Ultimately what it came down to was:
I needed to add the RedirectUri into my Web.config and into my Startup.Auth
Web.Config
<add key="ida:RedirectUri" value="https://sitename.azurewebsites.net/Home/Index"/>
Startup.Auth
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
RedirectUri = redirectUri,
PostLogoutRedirectUri = postLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = (context) =>
{
context.HandleResponse();
context.OwinContext.Response.Redirect("/Home/Index");
return Task.FromResult(0);
}
}
});
My Signin process kept routing back to my Account/Login page when failing, when it should have routed to my Home/Index, as I was using my Account/Login as my landing page and authentication only happening there after, the problem that happened here was because I did an "EnsureLogOut" on the Account/Login, thus why it kept logging me out first before wanting to authenticate. So instead of the Redirect = "/" I changed as follows:
public void SignIn()
`{`
`clsHomeScreen clsHomeScreen = new clsHomeScreen();`
`if (!Request.IsAuthenticated)`
`{`
`HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/Home/Index" }, OpenIdConnectAuthenticationDefaults.AuthenticationType);`
`}`
`Response.Redirect("/Home/Index");`
`}`
Perhaps this is something that might not help others, but maybe it helps them in the right direction.

asp.net core 2.0 cookie authentication with session variable

I'm trying to implement asp.net core cookie-based authentication. So I have added the code below to my startup.cs
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.AccessDeniedPath = new PathString("/login");
options.LoginPath = new PathString("/login");
options.SlidingExpiration = true;
});
and I'm sign-in using the code below
[HttpPost]
[ValidateAntiForgeryToken]
[Route("login")]
public async Task<IActionResult> Login(AuthViewModel authView)
{
if (ModelState.IsValid)
{
var (status, message, SigninUser) = await authentication.Authenticate(new User()
{
email = authView.Email,
pwd = authView.Password
});
if (status)
{
List<Claim> claims = new List<Claim>
{
new Claim(ClaimTypes.Name, "App Member"),
new Claim(ClaimTypes.Email, SigninUser.email)
};
ClaimsIdentity identity = new ClaimsIdentity(claims, "cookie");
ClaimsPrincipal principal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync(
scheme: CookieAuthenticationDefaults.AuthenticationScheme,
principal: principal,
properties: new AuthenticationProperties
{
IsPersistent = authView.RememberMe,
ExpiresUtc = DateTime.UtcNow.AddYears(1)
});
HttpContext.Session.Set<User>("session_user", SigninUser);
if (Url.IsLocalUrl(authView.returnUrl))
return Redirect(authView.returnUrl);
else
return RedirectToAction("Index");
}
else
{
authView.Status = false;
authView.Message = message;
}
}
else
{
string message = string.Join(" | ", ModelState.Values.SelectMany(e => e.Errors).Select(v => v.ErrorMessage));
authView.Status = false;
authView.Message = message;
}
return View(authView);
}
This works fine. But when I keep the browser IDLE for like 30 minutes the "session_user" session variable get expired and the user still gets authenticated. How can I resolve this?
Also using cookie-based authentication can have a performance penalty?
Thanks

Web Api with Owin with JWT always fails to authorize request

I have followed the tutorials up till this point in the series. I am using one project in the solution that acts as both the token issuing authority as well as the resource server.
The JWT is generated using the endpoint mentioned in the startup class and I validated it on jwt.io as well. However when I pass this JWT using Postman on Chrome to the resource API end point secured with an Authorize attribute, I always find it returning
{
"message": "Authorization has been denied for this request." }
The other api method as in the api controller class below works when called thru Postman on Chrome.
I have used the latest versions of all dlls required from the nuget console
Code in the startup class
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
ConfigureOAuthTokenGeneration(app);
ConfigureOAuthTokenConsumption(app);
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
private void ConfigureOAuthTokenGeneration(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
//For Dev enviroment only (on production should be AllowInsecureHttp = false)
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/oauth/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new CustomOAuthProvider(),
AccessTokenFormat = new CustomJwtFormat(ConfigurationManager.AppSettings["Issuer"]),
};
// OAuth 2.0 Bearer Access Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
}
private void ConfigureOAuthTokenConsumption(IAppBuilder app)
{
string issuer = ConfigurationManager.AppSettings["Issuer"];
string audienceId = ConfigurationManager.AppSettings["AudienceId"];
byte[] audienceSecret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["AudienceSecret"]);
// Api controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { audienceId },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, audienceSecret)
}
});
}
Code in the Custom OAuthProvider
public class CustomOAuthProvider : OAuthAuthorizationServerProvider
{
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
return Task.FromResult<object>(null);
}
public override Task MatchEndpoint(OAuthMatchEndpointContext context)
{
//avoid pre-flight calls
if (context.OwinContext.Request.Method == "OPTIONS" && context.IsTokenEndpoint)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Methods", new[] { "POST" });
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Headers", new[] { "accept", "authorization", "content-type" });
context.OwinContext.Response.StatusCode = 200;
context.RequestCompleted();
return Task.FromResult<object>(null);
}
return base.MatchEndpoint(context);
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
//setting up claims in the constructor of class UserDetails
UserDetails user = new UserDetails();
user.UserName = context.UserName;
user.FirstName = "Dummy First";
user.LastName = "Dummy Last";
ClaimsIdentity identity = new ClaimsIdentity("JWT-BearerAuth-Test");
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
foreach (string claim in user.Claims)
{
identity.AddClaim(new Claim(ClaimTypes.Role, claim));
}
var ticket = new AuthenticationTicket(identity, null);
context.Validated(ticket);
}
}
The custom JWT class
public class CustomJwtFormat : ISecureDataFormat<AuthenticationTicket>
{
private readonly string _issuer = string.Empty;
public CustomJwtFormat(string issuer)
{
_issuer = issuer;
}
public string Protect(AuthenticationTicket data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
string audienceId = ConfigurationManager.AppSettings["AudienceId"];
string symmetricKeyAsBase64 = ConfigurationManager.AppSettings["AudienceSecret"];
var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);
var signingKey = new HmacSigningCredentials(keyByteArray);
var issued = data.Properties.IssuedUtc;
var expires = data.Properties.ExpiresUtc;
var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingKey);
var handler = new JwtSecurityTokenHandler();
var jwt = handler.WriteToken(token);
return jwt;
}
}
The Resource server's Api controller
public class AdminController : ApiController
{
//This call works
public IHttpActionResult ReadData(string id)
{
return Ok("ID sent in:" + id);
}
//[Authorize(Roles="EditRecord")] //doesnt work
[Authorize] //doesnt work either
public IHttpActionResult EditData(string id)
{
return Ok("Edited ID:" + id);
}
}
My environment is VS2013 with Framework 4.5 using OAuth2 with Web Api 2. Please excuse the long post.
You need to make sure that values for issuer, audienceId, and audienceSecret used in method "ConfigureOAuthTokenConsumption" are the same values used when you generated the JWT token, take care of trailing slashes "/".
This is the only thing comes to my mind right now.

ASP.NET Identity 2.1 - Password Reset Invalid Tokens

ASP.NET Identity is returning an 'Invalid token.' response when resetting a password for users.
I've tried the following:
URL Encode the code before sending email
URL Encode & Decode the code before and after
Copying the code to make sure it matches what was sent
Ensured my user email is confirmed (I heard that could be a problem)
Created a custom UserManager/Store etc.
This is my email code:
var user = await UserManager.FindByNameAsync(model.Email);
var code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
var callbackUrl = Url.Action("ResetPassword", "Account", new { code }, "http");
var body = string.Format("Click here to reset your password: {0}", callbackUrl);
await UserManager.SendEmailAsync(user.Id, "Reset Password", body);
return View("~/Views/Account/Login.cshtml", model);
The generated URL:
http://localhost/Account/ResetPassword?code=XTMg3fBDDR77LRptnRpg7r7oDxz%2FcvGscq5Pm3HMe8RJgX0KVx6YbOeqflvVUINipVcXcDDq1phuj0GCmieCuawdgfQzhoG0FUH4BoLi1TxY2kMljGp1deN60krGYaJMV6rbkrDivKa43UEarBHawQ%3D%3D
Finally my reset code:
if (!ModelState.IsValid)
{
return View(model);
}
var user = await UserManager.FindByNameAsync(model.Email);
if (user == null)
{
// Don't reveal that the user does not exist
return RedirectToAction("ResetPasswordConfirmation", "Account");
}
var result = await UserManager.ResetPasswordAsync(user.Id, model.Code, model.Password);
if (result.Succeeded)
{
return RedirectToAction("ResetPasswordConfirmation", "Account");
}
ModelState.AddModelError("","Invalid Password Please Try Again");
return View();
Inside the result is 1 error, Invalid token.
My create UserManager method:
public static CustomerUserManager Create(IdentityFactoryOptions<CustomerUserManager> options, IOwinContext context)
{
var manager = new CustomerUserManager(new CustomerUserStore(context.Get<CustomerDbContext>()));
// Configure validation logic for usernames
manager.UserValidator = new UserValidator<Customer>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
manager.EmailService = new EmailService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider = new DataProtectorTokenProvider<Customer, string>(dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
My Startup.Auth config:
app.CreatePerOwinContext(CustomerDbContext.Create);
app.CreatePerOwinContext<CustomerUserManager>(CustomerUserManager.Create);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity =
SecurityStampValidator.OnValidateIdentity<CustomerUserManager, Customer, string>
(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager),
getUserIdCallback: (id) => (id.GetUserId())
)
}
});
List of tried solutions:
ASP.NET Identity 2 - UserManager.ConfirmEmail Fails In Production
aspnet identity invalid token on confirmation email
http://www.asp.net/mvc/overview/security/create-an-aspnet-mvc-5-web-app-with-email-confirmation-and-password-reset#reset
Asp.NET - Identity 2 - Invalid Token Error
aspnet identity invalid token on confirmation email
https://aspnetidentity.codeplex.com/discussions/544368
Thanks for any help with this problem.
You can try this code.
I shared this link: aspnet identity invalid token on confirmation email
var encodedCode= code.Base64ForUrlEncode();
var decodedCode= encodedCode.Base64ForUrlDecode();
public static class UrlEncoding
{
public static string Base64ForUrlEncode(this string str)
{
byte[] encbuff = Encoding.UTF8.GetBytes(str);
return HttpServerUtility.UrlTokenEncode(encbuff);
}
public static string Base64ForUrlDecode(this string str)
{
byte[] decbuff = HttpServerUtility.UrlTokenDecode(str);
return Encoding.UTF8.GetString(decbuff);
}
}

Getting Access_Denied response when using Owin.Security.Providers.LinkedIn.LinkedInAuthenticationProvider

I'm playing with Owin.Security.Providers project to allow my MVC 5 app sign in with various social networks. I created an app in LinkedIn and set its scope to r_emailaddress and r_basicprofile. When I try to sign in with LinkedIn accout, I get /Login/SigninRedirect?error=access_denied on my return URL. Though my CallbackPath which is set to /signin-redirect URL has code and state attributes. In Fiddler it looks like this: /signin-redirect?code=<code_id>&state=<state_id>.
Anyone has any ideas what I might be doing wrong?
Here is my code:
protected virtual void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
var linkedInAuthOptions = new LinkedInAuthenticationOptions()
{
ClientId = <client_id>,
ClientSecret = <client_secret>,
CallbackPath = new PathString("/signin-redirect")
};
app.UseLinkedInAuthentication(linkedInAuthOptions);
}
In my Login controller I handle sign in and the redirect similar to VS2013 example:
public class LoginController : Controller
{
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider, string returnUrl)
{
var redirectUrl = "/Login/SigninRedirect";
redirectUrl += string.IsNullOrEmpty(returnUrl) ? "" : "?redirectUrl=" + returnUrl;
return new ChallengeResult(provider, redirectUrl);
}
// Redirects from successful social signin to a speccified URL.
public ActionResult SigninRedirect(string redirectUrl)
{
var redirect = string.IsNullOrEmpty(redirectUrl) ? "/" : redirectUrl;
ExternalLoginInfo loginInfo = AuthenticationManager.GetExternalLoginInfo();
// loginInfo is null when trying to sign in with LinkedIn
// works fine for Twitter though
if (loginInfo != null)
{
var authResult = AuthenticationManager.AuthenticateAsync(DefaultAuthenticationTypes.ExternalCookie).Result;
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var claims = authResult.Identity.Claims.ToList();
claims.Add(new Claim(ClaimTypes.AuthenticationMethod, loginInfo.Login.LoginProvider));
var claimsIdentity = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ExternalCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = true }, claimsIdentity);
}
return new RedirectResult(redirect);
}
#region Helpers
private IAuthenticationManager AuthenticationManager
{
get
{
return HttpContext.GetOwinContext().Authentication;
}
}
private class ChallengeResult : HttpUnauthorizedResult
{
private const string XsrfKey = "XsrfId";
public ChallengeResult(string provider, string redirectUrl, string userId = null)
{
LoginProvider = provider;
RedirectUrl = redirectUrl;
UserId = userId;
}
public string UserId { get; set; }
public string RedirectUrl { get; set; }
public string LoginProvider { get; set; }
public override void ExecuteResult(ControllerContext context)
{
var properties = new AuthenticationProperties() {RedirectUri = RedirectUrl};
if (UserId != null)
{
properties.Dictionary[XsrfKey] = UserId;
}
context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
}
}
#endregion
}
The same code works fine when I try to sign in with Twitter account.
Found the issue. The problem was that I registered the same CallbackPath (/signin-redirect) for both Twitter and LinkedIn providers. Like this:
var twitterAuthOptions = new TwitterAuthenticationOptions()
{
ConsumerKey = <consumerKey>,
ConsumerSecret = <consumerSecret>,
CallbackPath = new PathString("/signin-redirect")
};
app.UseTwitterAuthentication(twitterAuthOptions);
var linkedInAuthOptions = new LinkedInAuthenticationOptions()
{
ClientId = <clientId>,
ClientSecret = <clientSecret>,
CallbackPath = new PathString("/signin-redirect")
};
app.UseLinkedInAuthentication(linkedInAuthOptions);
When I set different CallbackPath's, everything started working fine.

Resources