Authorized, but not authorized in OAuth - c#

I'm trying to make OAuth work for me for the first time. Using this as a basis I've created an out-of-the-box MVC app with the Authentication set to "Individual User Accounts". I've modified my Startup.Auth.cs file to look like this...
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
var googleOptions = new GoogleOAuth2AuthenticationOptions()
{
ClientId = "blah",
ClientSecret = "blah-blah",
CallbackPath = new PathString("/Account/ExternalLoginCallback"),
Provider = new GoogleOAuth2AuthenticationProvider
{
OnAuthenticated = async ctx =>
{
string accessToken = ctx.AccessToken;
string googleName = ctx.Name;
string googleEmailAddress = ctx.Email;
var serializedUser = ctx.User;
}
}
};
googleOptions.Scope.Add("https://www.googleapis.com/auth/drive.file");
app.UseGoogleAuthentication(googleOptions);
}
}
Obviously, in my real code I have real values for ClientId and ClientSecret.
When I run the app, the start page shows.
When I click on "Log In" I see an option to log in via Google.
Clicking that shows me the Consent screen.
But this is what I don't understand. When I click on Allow my app redirects to http://localhost/WebApplication2/Account/ExternalLoginCallback?error=access_denied#
I'm clearly doing something wrong, I just can't figure out what. Can anyone offer any clues?

Related

How to redact username password details on client browser when building MVC 5 application

I have built an MVC application and I am using its standard authentication code generated by the template. The configuration looks like:
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
ExpireTimeSpan = TimeSpan.FromDays(14),
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
SlidingExpiration = true,
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator
.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(TimeSpan.FromMinutes(30),
(manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
}
The problem is that when I have logged in if I use Chrome tools and look in the payload tab for the Account/Login?ReturnUrl=mysite.com/somepage, I see both the username and password in plain text.
Is there a way to redact this information? or even better not have it? or is this standard approach?

ASP.NET WebForms read IDToken after Azure Active Directory authentication

I have an old webforms asp.net web application, based on Identity 2.0 local authentication that I have to upgrade to allow also the authentication for external users registered in the Azure Active Directory of the company.
I'm able to run the challenge and get back the users on the web page after they authenticate on Microsoft, but I'm not able to read any info of the user. In example, I want to know their email in order to let them enter my application or register as new users.
I expect to have this information in the token, but how can I access it server-side?
Here is my code:
public partial class Startup {
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager,User>(
validateInterval: TimeSpan.FromSeconds(120),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager)),
OnApplyRedirect = ctx =>
{
ctx.Response.Redirect(ctx.RedirectUri);
}
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = "xxxxxxx-xxxx-xxxx-xxxxxxxxx",
Authority = "https://login.windows.net/xxxxxxx-xxxx-xxxx-xxxxxxxxx",
PostLogoutRedirectUri = "https://localhost:44364/testlogin.aspx",
Scope = OpenIdConnectScope.OpenIdProfile,
// ResponseType is set to request the id_token - which contains basic information about the signed-in user
ResponseType = OpenIdConnectResponseType.IdToken,
// ValidateIssuer set to false to allow personal and work accounts from any organization to sign in to your application
// To only allow users from a single organizations, set ValidateIssuer to true and 'tenant' setting in web.config to the tenant name
// To allow users from only a list of specific organizations, set ValidateIssuer to true and use ValidIssuers parameter
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = false
},
// OpenIdConnectAuthenticationNotifications configures OWIN to send notification of failed authentications to OnAuthenticationFailed method
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed
}
}
);
}
private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
{
context.HandleResponse();
context.Response.Redirect("~/TestLogin.aspx?ErrorMessage=" + context.Exception.Message);
return Task.FromResult(0);
}
}
and here is the call to login external active directory users:
Context.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties { RedirectUri = "~/TestLogin.aspx" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
and finally in the TestLogin.aspx page where I try to read information about the logged in users:
if (Request.IsAuthenticated) //Always False!
{
Label1.Text = System.Security.Claims.ClaimsPrincipal.Current.FindFirst("name").Value;
}
var userClaims = System.Security.Claims.ClaimsPrincipal.Current;
if (userClaims != null) //It's not null but there is no information about the email of the logged in user
{
Label1.Text += userClaims?.FindFirst("name")?.Value; //It's empty
}
How can I read the claims returned by active directory in the ID Token?
UPDATE
If I remove the option in the cookie authentication, Azure Active Directory works, but I'm not able anymore to sign in local users:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager,User>(
validateInterval: TimeSpan.FromSeconds(120),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager)),
OnApplyRedirect = ctx =>
{
ctx.Response.Redirect(ctx.RedirectUri);
}
}
});
into this:
app.UseCookieAuthentication(new CookieAuthenticationOptions());
Is there a way to make them both works?

How do I handle an Authorization Error in Google OAuth2 Authentication?

I need some help in handling Google Authentication errors.
I followed this tutorial for integrating Google authentication in my ASP.NET MVC 5 web application. I limited the application type to internal use, so only colleagues can log in with their work account (we have our company e-mail in GMail) and all is working well, as long as I log in with my work account.
When I try to log in with a different (non-company) account, I get an Authorization error, as expected. My problem is that the error is a page from Google and there is no callback to my application.
I've searched the internet for quite some time, but I can't seem to find how to set up my application that I get a callback on an authorization error. I'm beginning to wonder if it's even possible...
Can someone help me resolve this? I'm not sure if it's relevant, but here is the ConfigureAuth class in my Startup.Auth.cs.
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
GoogleOAuth2AuthenticationOptions _options = new GoogleOAuth2AuthenticationOptions()
{
ClientId = "clientId.apps.googleusercontent.com",
ClientSecret = "clientSecret"
};
_options.Scope.Add("profile");
_options.Scope.Add("email");
_options.CallbackPath = new PathString("/Account/LoginResult");
_options.Provider = new GoogleOAuth2AuthenticationProvider()
{
OnAuthenticated = async context =>
{
string claimType;
bool bAddClaim = false;
foreach (var claim in context.User)
{
claimType = string.Empty;
bAddClaim = false;
switch (claim.Key)
{
case "picture":
claimType = "Picture";
bAddClaim = true;
break;
}
if (bAddClaim)
{
string claimValue = claim.Value.ToString();
if (!context.Identity.HasClaim(claimType, claimValue))
context.Identity.AddClaim(new System.Security.Claims.Claim(claimType, claimValue, "XmlSchemaString", "Google"));
}
}
}
};
app.UseGoogleAuthentication(_options);
}
I don't know what other code is relevant, so please ask if you're missing something.
Thanks in advance!

Facebook Login using Owin Identity

I'm trying to make a facebook login but whatever I do I keep getting an access denied error.
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
var facebookOptions = new FacebookAuthenticationOptions();
facebookOptions.AppId = "appId";
facebookOptions.AppSecret = "appSecret";
facebookOptions.Scope.Add("email");
app.UseFacebookAuthentication(facebookOptions);
}
}
Facebook Login button
protected void FacebookLogin_Click(object sender, ImageClickEventArgs e)
{
var properties = new AuthenticationProperties() { RedirectUri = "Login.aspx" };
Context.GetOwinContext().Authentication.Challenge(properties, "Facebook");
}
When I press the button I get redirected to facebook and once I login in facebook I get redericted back to the login page but in de querystring I keep getting the access denied error. I'm pretty sure my login details are correct because when I press the button a second time I don't see the facebook page anymore I am immediatly redirected back to the login page.
Why do I keep getting the access denied error. I have set everything in the facebook also using these instructions
I sure hope anyone can help me on this.

Use Azure AD only for Authentication and not Authorization

I've been messing with this for a few days now...
What I would like to do is Authenticate users with Azure AD, and when successful, automatically log them in using ASP.NET Identity for authorization. If they do not have an account I would like to create one automatically.
Essentially Azure AD is just confirming that they are a part of the organization, the ASP.NET Identity portion is it's own database where I can use the [Authorize] attribute to set up custom roles OUTSIDE of Azure AD.
This is my ConfigureAuth() method:
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(IntranetApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = SettingsHelper.ClientId,
Authority = SettingsHelper.Authority,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
// If there is a code in the OpenID Connect response, redeem it for an access token and refresh token, and store those away.
AuthorizationCodeReceived = (context) =>
{
var code = context.Code;
ClientCredential credential = new ClientCredential(SettingsHelper.ClientId, SettingsHelper.AppKey);
String signInUserId = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
AuthenticationContext authContext = new AuthenticationContext(SettingsHelper.Authority, new ADALTokenCache(signInUserId));
AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, SettingsHelper.AADGraphResourceId);
return Task.FromResult(0);
},
RedirectToIdentityProvider = (context) =>
{
// This ensures that the address used for sign in and sign out is picked up dynamically from the request
// this allows you to deploy your app (to Azure Web Sites, for example)without having to change settings
// Remember that the base URL of the address used here must be provisioned in Azure AD beforehand.
string appBaseUrl = context.Request.Scheme + "://" + context.Request.Host + context.Request.PathBase;
context.ProtocolMessage.RedirectUri = appBaseUrl + "/";
context.ProtocolMessage.PostLogoutRedirectUri = appBaseUrl;
return Task.FromResult(0);
},
AuthenticationFailed = (context) =>
{
// Suppress the exception if you don't want to see the error
context.HandleResponse();
return Task.FromResult(0);
}
}
});
// 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))
},
});
}
Right now the ASP.NET Identity is taking over when I do a HttpContext.Request.IsAuthenticated which is okay, I just need a way to check if the OpenID portion is authenticated or not so I can put in my custom logic to automatically sign the user in.
Got it!
My biggest problem was attempting to use the OWIN middleware to do everything for me. The OpenID middleware is not needed for simple authentication to Azure AD. I essentially created a OpenIdAuth method in the Account controller which acts as my in-between to authenticate the user with Azure before they have access to the site.
[AllowAnonymous]
public ActionResult OpenIdAuth(string code)
{
string clientId = "00000000-0000-0000-0000-000000000000"; // Client ID found in the Azure AD Application
string appKey = "111111111112222222222223333333333AAABBBCCC="; // Key generated in the Azure AD Appliction
if (code != null)
{
string commonAuthority = "https://login.windows.net/<TENANT_URL>"; // Eg. https://login.windows.net/MyDevSite.onmicrosoft.com
var authContext = new AuthenticationContext(commonAuthority);
ClientCredential credential = new ClientCredential(clientId, appKey);
AuthenticationResult authenticationResult = authContext.AcquireTokenByAuthorizationCode(code, new Uri(HttpContext.Request.Url.GetLeftPart(UriPartial.Path)), credential, "https://graph.windows.net");
var userManager = HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
var signInManager = HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
var user = UserManager.FindByName(authenticationResult.UserInfo.UniqueId);
if (user != null)
{
signInManager.SignIn(user, false, false);
}
else
{
var newUser = new ApplicationUser { UserName = authenticationResult.UserInfo.UniqueId, Email = authenticationResult.UserInfo.DisplayableId };
var creationResult = UserManager.Create(newUser);
if (creationResult.Succeeded)
{
user = UserManager.FindByName(newUser.UserName);
signInManager.SignIn(user, false, false);
}
else
{
return new ViewResult { ViewName = "Error" };
}
}
return Redirect("/");
}
else
{
var url = new Uri($"https://login.microsoftonline.com/<TENANT_URL>/oauth2/authorize?client_id={clientId}&response_type=code&redirect_uri=https://localhost/Account/OpenIdAuth");
return Redirect(url.AbsoluteUri);
}
}
The awesome part is the code variable that will be passed by Microsoft when the user logs in successfully. (As documented Here) I used the same controller method and checked if it was null but technically two different controller methods can be used (Microsoft will redirect back to the url you specify for the redirect_uri parameter).
After I got the authorization code I can use the AuthorizationContext from the Microsoft.IdentityModel.Clients.ActiveDirectory nuget package to call: AcquireTokenByAuthorizationCode. The last parameter is the Resource URI. I'm using the Graph Resource but you can use any other resource that you've given your app access to in the Azure Management Portal.
Finally my ConfigureAuth Method is back to the plain ol' ASP.NET Identity version:
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(IntranetApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
// 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))
},
});
}

Categories

Resources