I have an MVC client app (APP1) protected by Identity Server. I use a backchannel mechanism to log out of clients as soon as user logs out of idsrv. Pretty similar to this one: https://github.com/IdentityServer/IdentityServer4/blob/main/samples/Clients/src/MvcHybridBackChannel/Startup.cs
Now the thing is: I need to add a support for external provider, Azure AD. And in turn it also should support automatic log out: when a user logs out of Azure AD, he should be logged out of idsrv and from the client apps.
My first idea was to implement the same approach: Azure App registration supports the ability to call client's endpoint upon log out. But I'm struggling with the part when I need to set up custom CookieAuthenticationEvents. The code which I have in a client app APP1:
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
}).AddCookie("Cookies", options =>
{
options.EventsType = typeof(CookieEventHandler);
}
But the same code doesn't work in idsrv. But this I mean that my cookie is not being validated by my custom CookieEventHandler. Can anyone point me in a right direction?
One idea is to let Azure AD call the Logout method on the AccountController to sign out the user. (or if you make your own "logout" endpoint).
The logout method is protected by a ValidateAntiForgeryToken, so you might need to get around that by creating your "own" logout endpoint.
But basically what you need to do on a high level is to let AzureAD trigger a call to the
HttpContext.SignOutAsync(); method.
Related
I need to add OpenID Connect authentication to an existing ASP.NET MVC web application. For that, I am using OWIN.
Once the user has been successfully authenticated by the authentication server (Azure Active Directory in my case), I need to check that the user can be mapped to a local account in my web application. If the user has no valid local account, the actual login process should fail. Is there a standard way to do that?
In the sample I have found (https://learn.microsoft.com/en-us/azure/active-directory/develop/tutorial-v2-asp-webapp), it seems that as soon as the user is successfully authenticated by Azure, then it is logged in the application. Can we add custom code to the OWIN-OIDC sign-in process to perform additional checks?
Thanks for your help.
Good question and there are 2 levels here, as you are finding:
Authenticating with the Authorization Server / Identity Provider
Access to the application being allowed
If the first is successful but the second is not, then the standard behaviour is to redirect the user to an 'Access Denied' page (this page must allow anonymous access, to avoid a redirect loop). The login has not failed, but the user is not authorized to access the app.
OWIN uses response_type='code id_token' meaning that you will have an Open Id Connect User Identity to work with. If you can't find the user in your own data you can perform a redirect in the below callback:
var openidOptions = new OpenIdConnectAuthenticationOptions
{
Notifications = new OpenIdConnectAuthenticationNotifications
{
// Point to a callback to check the identity against my own system
// This callback can perform a redirect if the app does not recognise the user
AuthorizationCodeReceived = HandleAuthorizationCodeReceived
}
}
I'm trying to build a ASP.NET MVC app which doesn't allow access to anonymous user (with the exception of a custom URL that is to be shown to authenticated users which aren't allowed to use the app).
Now, I've register my app in the azure portal (portal.azure.com) and I'd like to use several URLs. I've added two entries:
https://localhost/test
https://www.test.com
I'm using the following code to configure authentication at startup:
public void ConfigureAuth(IAppBuilder app) {
app.SetDefaultSignInAsAuthenticationType(
CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions {
ClientId = clientId,
Authority = authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications {
SecurityTokenValidated = VerificaUtilizadorAutenticado,
AuthenticationFailed = TrataErroAutenticacao
}
});
}
Everything seemed to be working fine, but after publishing the app, it seems like I can only use one of the URIs. I've searched and it seems like I can use the RedirectUri property of the OpenIdConnectAuthenticationOptions to set up the reply url. So, I've tried adding this to the setup:
RedirectUri = "https://localhost/test",
Unfortunately, doing that breaks everything and the browser gets stuck between my app and MS' login page. Since the user is logged in, it redirects the user back to my app. However, it seems like setting the RedirectUri prop does not generate the app's authentication cookie, so it sends the user back to the login page.
If I remove the RedirectUri, then I get redirected to the https://www.test.com site, even though I'm trying to access the https://localhost/test web app.
I'm not sure what, but I'm missing something...Can anyone help?
Thanks.
Luis
When you send a user who wants to sign in to the AAD Login Endpoint (https://login.microsoftonline.com), you will need to specify where you want the user (and the Authorization Code) to be redirected back to.
You specify this in two ways:
You must configure a Reply URL in your app's main configuration as a part of app creation.
You must specify a Reply URL in your Login URL, which exactly matches one of the URLs you configured:
From the Authorization Code Grant Flow documentation:
https://login.microsoftonline.com/{tenant}/oauth2/authorize?
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&response_type=code
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F <-- this guy
&response_mode=query
&resource=https%3A%2F%2Fservice.contoso.com%2F
&state=12345
If you do not specify a Redirect URI in your Login URL, the user will be redirected to the first URL specified in your app's registration. That doesn't sound so bad, but know that AAD treats your Reply URLs as an unordered list, so depending on the server you hit, or how data got replicated, you might observe different redirection behaviors across different sign-in attempts. That is why it is important to ALWAYS specify the redirect_uri parameter in your login endpoint.
I feel like many of your problems may be solved if you use the ADAL libraries to authenticate rather than the libraries shown in your sample code, but if you do not want to use these, then you will need to share more details like the HTTP trace of the authentication process.
Ok, after some digging, I've noticed that the problem I was having when setting the redirecturi was that I was missing...a slash (/). So, adding the / to the URL defined on the portal and putting it also in the redirecturi of the options object was enough to make it work against different URI...
I have implemented a AWS Lambda function and used the gateway to return the fulling data:
var param =
{
IdentityPoolId: "actualIdentityPoolId",
Logins: {} // To have provider name in a variable
};
param.Logins["com.testing.userLogin"] = userId;
cognitoidentity.getOpenIdTokenForDeveloperIdentity(param,
function(err, data)
{
if (err) return fn(err); // an error occurred
else fn(null, data.IdentityId, data.Token); // successful response
});
So the identityId and token get sent back to the ios device. In my device I try to connect to an AWS DynamoDB table but access is denied. How do I use the identityId and token to gain access to the tables?
I have set up roles in IAM for Unauth which denies Dydnamo and Auth which gives access to the tables through its policies.
I am trying to implement authentication using: http://docs.aws.amazon.com/cognito/latest/developerguide/authentication-flow.html
I see there are two flows which are Basic and Enhanced. The documentation says most users will use the enhanced flow and that implements GetCredentialForIdentity.
How is that implemented in my ios code so that I can switch my role from unauth to auth and can access to dynamodb? How long will this access last? I would like to do this all in my ios code instead of using lambda or something else like that.
If your user is unauthenticated, then logs in you need to clear your credentials, and your 'logins' method should now return a properly updated logins map.
Here is the documentation to help you:
http://docs.aws.amazon.com/cognito/latest/developerguide/developer-authenticated-identities.html
Double check your DynanoDB Roles for authenticated access your DynamoDB resource. An example role for this are on the following page of the developer guide you referenced. The page is called "IAM Roles" and the last section is the important one: "Fine-Grained Access to Amazon DynamoDB".
Stick with your plan to use the Enhanced Authflow. It is recommended and makes less calls to authenticate (your users will appreciate this). Just make sure you mobile clients call GetCredentialsForIdentity from iOS.
From the Enhanced Authflow documentation further down your page:
The GetCredentialsForIdentity API can be called after you establish an identity ID. This API is functionally equivalent to calling GetOpenIdToken followed by AssumeRoleWithWebIdentity.
The AssumeRoleWithWebIdentity is the important piece that allows your user to assume the Role that gets access to the DynamoDB resource. Cognito will take care of the rest as long as you set up the Roles correctly within the Cognito console:
In order for Amazon Cognito to call AssumeRoleWithWebIdentity on your behalf, your identity pool must have IAM roles associated with it. You can do this via the Amazon Cognito Console or manually via the SetIdentityPoolRoles operation (see the API reference)
Secure 2 different web applications with one identify service
Identity Service: Thinktecture Identity Service V2
Application 1: Asp.net MVC 5 application
Application 2: Asp.net Web API application
The above Applcation 1 and Application 2 are different projects and hosted in different servers. Now my scenarios are
1. Want to secure Application 1 with Identity Service.
2. Want to secure Application 2 with Identity Service.
Use case 1: If user access Application 1 it should redirect to identity service login page, once I entered the credentials and login in to application 1, the same token allow me to access Application 2.
Use case 2: If user try to access any API from Application 2 without login into Identity service, the request should reject.
I found a way to do this using pure SAML tokens. The trick is you need to create a delegation account in Identity Server to allow your web app to delegate identity to a specific realm (where your service lives). Then in the web app you make a service call using the the token that the user already has to get a new token which you use to access your service.
I asked a very similar question and answered it myself here.
Ok. I did eactly the same thing just now. Everything required to get that done is written here. If you're using IdentityServer, you need to configure the Token Type of your RP to be JWT:
this allows you to later extract the token from your authenticated MVC 5 application (see the link above to see how to do this) and then send that token to your Web API. You then need to tell you web api to accept that token, using Microsoft's JwtSecurityTokenHandler class. This class has a ValidateToken() method which accepts 2 parameters, the first being the access token that you put into your auth headers of the requests to the Web API, and the second, the validation parameters are basically what you've defined in IdentityServer's config:
validationParams = new TokenValidationParameters
{
AllowedAudiences = _allowedAudiencesAndSigningKeys.Select(x => x.Key),
ValidIssuer = ConfigurationManager.AppSettings["IssuerIdentity"],
ValidateIssuer = true,
SigningTokens = _allowedAudiencesAndSigningKeys.Select(x => new BinarySecretSecurityToken(Convert.FromBase64String(x.Value)))
};
The Audience(s)/Realm(s) you want to allow access to, the issuer name (your Identity Server name) and the signing symmetric key(s) of the applications you have defined in Identity Server and want to grand access to. The ValidateToken() method returns an ClaimsPrincipal with a list of the claims extracted from the token. The code to do all this can be put in a message handler:
public static void Configure(HttpConfiguration config)
{
var authNConfig = new AuthenticationConfiguration();
config.MessageHandlers.Add(new MyTokenValidationHandler());
}
I'm developing a REST service that uses MS Azure Access Control Service for authentication. If the examples are any indication, the typical way to secure a REST service this way would be to provide a global username and pw, private key, or X.509 cert for the protected service. However, I want to use the passive user login mechanism on a mobile device with a flow more like the following:
Unauthenticated user attempts to access protected service from app
Mobile app redirects to browser app (or embedded browser)
User selects identity provider to use for login (facebook, google, etc.) from ACS login page
User enters credentials for identity provider
Browser redirects back to app
App somehow gets the SWT token to use with subsequent REST requests.
I'm stuck at about step 5--getting the SWT token, and the existing examples I've found don't seem to address this scenario. In addition, I'm actually trying to build a proof of concept with a desktop client in WPF, which may complicate things. Can anyone suggest a specific tutorial or a path to pursue that uses the per-user authentication vs. per-service? Thanks.
EDIT:
As I'm digging into this deeper, I've realized that the examples posted below (and most others) are based on OAuth WRAP, which has been deprecated in favor of OAuth 2.0. Can anyone suggest a more up to date reference? Googling has turned up http://blogs.msdn.com/b/adventurousidentity/archive/2011/09/18/acs-v2-oauth-2-0-delegation-support-explained.aspx and http://connect.microsoft.com/site1168/Downloads/DownloadDetails.aspx?DownloadID=32719 but they're not the most intuitive.
You should look into the ACS Windows Phone sample:
http://msdn.microsoft.com/en-us/library/gg983271.aspx
Here instead of using Silverlight you will be using WPF. Most of the code should be re-usable. Note that since you are using WPF you will need to register your own object for scripting e.g:
[ComVisibleAttribute(true)]
public class NotifyHandler
{
public void Notify(string notifyString)
{
// Here I have the token.
}
}
this.webBrowser1.ObjectForScripting = new NotifyHandler();
Update:
The sample above uses OAuth Wrap to contact the secured service. If you would like to use OAuth2 you should change the way the "Authorization" header set:
OAuth WRAP case:
WebClient client = new WebClient();
client.Headers["Authorization"] = "OAuth " + _rstrStore.SecurityToken;
OAuth2 case:
WebClient client = new WebClient();
client.Headers["Authorization"] = string.Format("OAuth2 access_token=\"{0}\"", token);
You can use the "Simple Service" sample as a guide to implement your token validation in your REST service:
http://msdn.microsoft.com/en-us/library/gg185911.aspx
Yet if you would like to implement a more complete sample you can look at how CustomerInformationService is protected in the CTP version 1.4:
https://connect.microsoft.com/site1168/Downloads/DownloadDetails.aspx?DownloadID=35417
Take a look at this one:
WPF Application With Live ID, Facebook, Google, Yahoo!, Open ID
http://social.technet.microsoft.com/wiki/contents/articles/4656.aspx