I have a bunch of web apps that I've created. Some of them use old-style webforms authentication, and some other sites at our company use different authentication patterns.
I'm trying to consolidate this, under one SSO pattern using Azure Active Directory. I've been trying to following guides/tutorials but it's not clicking for me.
My tech is currently ASP 4/MVC 5, although if ASP 5/MVC 6 is easier then I have the freedom to go that route as well. All web apps are hosted in Azure currently.
The confusion for me comes in that while looking through documentation, there seem to be so many ways to authentication and authorize users (also, authenticate vs authorize isn't absolutely clear to me).
I went to the Active Directory area of Azure Management portal (the old one). I added a new application called TestApp. I set the app URL to https://localhost:44320/, then sign-on URL to https://localhost:44320/, the tenant name to testapp. And my reply URL is https://localhost:44320/
This makes my app id uri https://localhost:44320/testapp I think? I also have my client ID guid.
The tutorial has an AccountController with a SignIn method like this:
public void SignIn()
{
// Send an OpenID Connect sign-in request.
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/" }, OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
When navigating to this, I receive the following in the browser:
[InvalidOperationException: IDX10803: Unable to create to obtain configuration from: 'https://localhost:44320/testapp/.well-known/openid-configuration'.]
I have a feeling it's because Azure is unable to redirect this all to back to my localhost? How can I set this up so I can test it out on Azure itself? And even further than that, will this solution be usable from multiple webapps? I'd assume they'd each be different applications in my Active Directory, but they'd all need to use a SSO procedure, where users can sign into multiple apps with one identity.
Sorry for any confusion, this is all very convoluted to me but I am trying to learn it as best I can.
Edit:
In the Startup of the webapp, I am calling this:
private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
private static string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
private static string tenant = ConfigurationManager.AppSettings["ida:Tenant"];
private static string postLogoutRedirectUri = ConfigurationManager.AppSettings["ida:PostLogoutRedirectUri"];
string authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
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
{
AuthenticationFailed = context =>
{
context.HandleResponse();
context.Response.Redirect("/Error?message=" + context.Exception.Message);
return Task.FromResult(0);
}
}
});
}
Which utilizes these app.config settings:
<add key="ida:ClientId" value="[my client id here]" />
<add key="ida:Tenant" value="testapp" />
<add key="ida:AADInstance" value="https://localhost:44320/{0}" />
<add key="ida:PostLogoutRedirectUri" value="https://localhost:44320/" />
Azure is able to redirect to localhost, it will just pop up a security confirmation asking if its ok to navigate to localhost.
Your tenant in app.config doesn't look right, change these app settings:
<add key="ida:Tenant" value="[YOUR TENANT].onmicrosoft.com" />
<add key="ida:AADInstance" value="https://login.microsoftonline.com/{0}" />
To find out more about your tenant see this article: How to get an Azure Active Directory tenant
You can also try adding this code to your Notifications in Startup (just under AuthenticationFailed), try putting breakpoints on the handlers to see what happens:
AuthenticationFailed = context =>
{
context.HandleResponse();
context.Response.Redirect("/Error?message=" + context.Exception.Message);
return Task.FromResult(0);
},
SecurityTokenValidated = (context) =>
{
return Task.FromResult(0);
}
Put an [Authorize] attribute on one of your controllers and it should redirect to the AAD authentication when you browse to it.
AFAIK each app would need a separate application in Azure AD and you would need to implement this Authentication in each separate app. I have managed to have a seamless sign in experience when I link via url from one app to another.
This answer sums up authentication vs authorization nicely: Authentication versus Authorization
Related
I am trying to get Azure AD connected service working for my dotnet framework applications. I went through the wizard and it added the necessary dependencies and files I need for it to work. The issue I am having is it does not reliably work. So I've rolled back and I am just working locally. If I type in localhost/Athena it does not work, however, it leaves /signin-oidc at the end of the URL. When I manually delete that last part the page works fine. The error I am getting is
IDX21323: RequireNonce is 'System.Boolean'.
OpenIdConnectProtocolValidationContext.Nonce was null.
If I type in https://localhost/Athena it works every time. Below is the code from my Startup.Auth.cs file.
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = "8675309",
Authority = authority,
CallbackPath = new PathString("/signin-oidc"),
//Tried with the below redirecturi and I still have the same issues.
//RedirectUri = "https://localhost/Athena/signin-oidc"
});
}
This exception is occured when an OpenIdConnect middleware encounters an invalid nonce or a missing nonce cookie.
Try making following configurations.
Configure startup.cs as below
AuthenticationType = “ApplicationCookie”,
CookieSameSite = SameSiteMode.None,
CookieSecure = CookieSecureOption.Always
Check/configure web.config:
<system.web>
<sessionState cookieSameSite=”None”/>
<httpCookies requireSSL=”true” />
</system.web>
note:Make sure all your website traffic is over https.
The initialization code is different depending on the platform. For ASP.NET Core and ASP.NET, signing in users is delegated to the OpenID Connect middleware. Some configuration is required to adapt them to the Microsoft identity platform.
The code related to authentication in an ASP.NET web app and web APIs is located in the App_Start/Startup.Auth.cs file.
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
// Authority` represents the identity platform endpoint - https://login.microsoftonline.com/common/v2.0.
// `Scope` describes the initial permissions that your app will need.
// See https://azure.microsoft.com/documentation/articles/active-directory-v2-scopes/.
ClientId = clientId,
Authority = String.Format(CultureInfo.InvariantCulture, aadInstance, "common", "/v2.0"),
RedirectUri = redirectUri,
Scope = "openid profile",
PostLogoutRedirectUri = redirectUri,
});
}
You can refer the following document to know detailed information
/configuration in web.config file related to above Startup.auth.cs configuration
https://learn.microsoft.com/en-us/azure/active-directory/develop/scenario-web-app-sign-user-app-configuration?tabs=aspnet
The Microsoft identity platform implementation of OpenID Connect has
a few well-defined scopes .You can refer this
https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent
to know scopes and permissions in microsoft identity platform
I am trying to implement authentication using Azure AD. In the application setting I am setting the Reply URLs as https://example.com/myapp/login.aspx.
When I login it redirects me to https://example.com and not specified URL https://example.com/myapp/login.aspx
How can I make sure that it redirects at proper URL? Following is the code for Startup.
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = ConfigurationManager.AppSettings["owin:ClientId"].ToString(),
Authority = "https://login.microsoftonline.com/yz5036e3-2951-4c11-af4d-da019fa6a57d",
RedirectUri = ConfigurationManager.AppSettings["RedirectUri"].ToString()
});
}
How do you trigger the sign in flow? If you are following the samples and initiating the sign in by invoking Challenge as shown in https://github.com/Azure-Samples/active-directory-dotnet-webapp-openidconnect/blob/master/WebApp-OpenIDConnect-DotNet/Controllers/AccountController.cs, you might want to make sure that the RedirectUri in the AuthenticationProperties points to the URL you ultimately (as in, AFTER auth) want to land on.
I know, it's incredibly confusing - the RedirectUri property in the OIDC options point to the redirect you want to use in the auth protocol, the one on which you want to receive the auth token- and the one in the AuthenticationProperties is local URL you want to be redirected to after your exchange with the identity provider successfully concluded. The proerties have the same name for historical reasons.
In my case website was under virtual directory (converted in application). For login URL e.g. http://example.com/myapp/login.aspx, it was redirecting user to http://example.com. If I set RedirectUri as myapp/AfterLogin.aspx, it worked.
HttpContext.Current.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties { RedirectUri = "myapp/AfterLogin.aspx", },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
We have a web application written in ASP.NET that uses MVC for serving our Single Page Applications and Web API for ajax calls.
The authentication uses Microsoft.Owin and OpenIdConnect with Azure AD for Authority. The OAUTH flow is server side code authorization.
Then in Startup.Auth.cs we have
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
var cookieAuthenticationOptions = new CookieAuthenticationOptions()
{
CookieName = CookieName,
ExpireTimeSpan = TimeSpan.FromDays(30),
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
SlidingExpiration = true,
};
app.UseCookieAuthentication(cookieAuthenticationOptions);
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
AuthorizationCodeReceived = (context) =>
{
/*exchange authorization code for a token
stored on database to access API registered on AzureAD (using ADAL.NET) */
},
RedirectToIdentityProvider = (RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context) =>
{
/* Set the redirects URI here*/
},
});
}
When clicking on signin we navigate to an url whose routes map to the methods of the following MVC controller
public class AccountController : Controller
{
public void SignIn(string signalrRef)
{
var authenticationProperties = /* Proper auth properties, redirect etc.*/
HttpContext.GetOwinContext()
.Authentication.Challenge(authenticationProperties, OpenIdConnectAuthenticationDefaults.AuthenticationType, CookieAuthenticationDefaults.AuthenticationType);
}
public void SignOut(string signalrRef)
{
var authenticationProperties = /* Proper auth properties, redirect etc.*/
HttpContext.GetOwinContext().Authentication.SignOut(authenticationProperties,
OpenIdConnectAuthenticationDefaults.AuthenticationType, CookieAuthenticationDefaults.AuthenticationType);
}
Then the end-user connected to our application is authenticated between our client apps and the ASP.net server using an ASP.NET cookie. We would like to use Token Based approach instead. If you are interested this is the reason.
I tried to replace
the Nuget package Microsoft.Owin.Security.Cookies by Microsoft.Owin.Security.OAuth and in Startup.cs
replacing
app.UseCookieAuthentication(cookieAuthenticationOptions); by app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
and in my AccountController we changed the challenge from HttpContext.GetOwinContext().Authentication.SignOut(authenticationProperties,
OpenIdConnectAuthenticationDefaults.AuthenticationType, CookieAuthenticationDefaults.AuthenticationType); to HttpContext.GetOwinContext().Authentication.SignOut(authenticationProperties,
OpenIdConnectAuthenticationDefaults.AuthenticationType, OAuthDefaults.AuthenticationType);
The problem is that with Cookie the set-cookie was automatically sent in web request respond when the flow completes while redirecting to the url we specified.
Where can I find the Bearer generated by OWIN with UseOAuthBearerAuthentication (if there is any) **, **Where and When should I send it back to my client SPAs
Note: an open source sample of what we are trying to do can be found in this github repository.
I think there are two approaches for you to consider.
Use javascript libraries to perform sign-in & token acquisition within your single page app. Then your backend is purely an web API, and can just use OAuth bearer middleware to authenticate requests. The backend doesn't know anything about signing the user in. We have a good sample that takes this approach here. If your backend needs to make API calls as well, you could consider the OnBehalfOf flow as well. I usually recommend this approach.
Use the OpenIDConnect middleware in your server to perform user sign-in and token acquisition. You might even be able to omit the usage of the CookieAuthenticationMiddleware entirely (although I'm not 100% sure). You can capture the token in the AuthorizationCodeReceived notification as you mention, and you could redirect back to your SPA with the token in the fragment of the URL. You could also have some route which delivers the tokens (which are cached on your server) down to your javascript. In either case, you'll need to ensure that an outside caller can't get access to your tokens.
The thing to keep in mind will be how you refresh tokens when they expire. If you use #1, most of it will be handled for you by libraries. If you use #2, you'll have to manage it more yourself.
[Authorize]
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "Simple" , "Test"};
}
}
This is a simple "Web API 2" app.
public void ConfigureAuth(IAppBuilder app)
{
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
TokenValidationParameters = new TokenValidationParameters
{
SaveSigninToken = true,
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"]
},
});
}
As shown the application is using the WindowsAzureActive Directory authentication and Authorization.
Note that it works normally when published to Azure Api App, but always denies the request when in localhost.
I am not sure what happened, it used to work before.
Regards
Normally this is due to a mismatch in the audience you expect in the web API (the value you set via ValidAudience) and what you get in the incoming token. The value in the token reflects the resource identifier you used when requesting the token from the client.
Do you change the client code to request a different audience when calling the localhost instance vs the Azure API one?
Also, how do you publish the API to Azure? If you use VS, and in the Publish wizard settings you have the checkbox "use organizational auth" checked, the deployed web API will have a different audience value in its web.config.
Here is my Scenario.
I have authentication web-services exposed by another domain. Now I want user name and password to be sent to that external domain for authentication. and when user is authenticated (returned true), I want the ASP.net to take that authentication further and let the user in and provide me all the asp.net standard utilities accessible, like currentuser, Isauthorized, Roles etc, for the user, authenticated. I hope this make sense.
This is not a problem. You have a variety of options available to you. One approach is to blend Forms Authentication with your own security model.
The basic idea is to let Forms Auth create and manage a ticket (in the form of an encrypted ticket) for the logged-in user. The ticket is used to determine whether or not someone is logged in, and who they are. You can then mix in any additional security related logic on top of that.
To process the login request, you just have a controller and action like you normally would. Note: in the example below, I am making some assumptions about LoginViewModel, the service you are using to authenticate, and the object it returns if any. You'll have to sub in your actual logic.
public ActionResult Login(LoginViewModel model)
{
// make sure the user filled out the login fields correctly
if (!ModelState.IsValid) return View(model);
// authenticate the user here
var authenticatedUser = AuthorizeUserUsingRemoteWebService(model.Username, model.Password);
if (authenticatedUser.IsAuthenticated)
{
// create forms auth ticket cookie and redirect to the home page
FormsAuthentication.SetAuthCookie(authenticatedUser.Username);
return RedirectToAction("Index", "Home");
}
// authentication failed, so show the login page again
return View(model);
}
In addition to that, you may have an HTTP module that handles the AuthenticateRequest event. Your module will be registered after the Forms Auth HTTP module, so it will have already processed whether or not the user is logged in. What you want to do is look up additional information if they are logged in, to get roles and such.
public class CustomAuthHttpModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.AuthenticateRequest += new EventHandler(OnAuthenticateRequest);
}
void OnAuthenticateRequest(object sender, EventArgs e)
{
HttpApplication application = (HttpApplication)sender;
HttpContext context = appObject.Context;
// user isn't logged in, so don't do anything else
if (!context.User.Identity.IsAuthenticated) return;
// look up the roles for the specified user, returning the role names as an array of strings
string[] roles = LookupUserRolesFromWebService(context.User.Identity.Name);
// replace the current User principal with a new one that includes the roles we discovered for that user.
context.User = new GenericPrincipal(new GenericIdentity(context.User.Identity.Name), roles);
}
}
You'll register the HTTP module in your web.config:
<httpModules>
<add name="CustomAuthHttpModule"
type="MyAssembly.CustomAuthenticationModule, MyAssembly" />
</httpModules>
You can now use the User object in your MVC controllers and views, the AuthenticatedAttribute, etc.
However, I'd recommend that you cache the results of looking up a user's roles so you don't hammer your web service. I'll leave that up to you.
You can you use Security Token Service for your application. Setup a Windows Identity Foundation SDK and find examples in sdk directory (for me it is "C:\Program Files (x86)\Windows Identity Foundation SDK\v4.0\Samples\End-to-end\Federation for Web Apps"). One of them ( named "Federation for Web Apps") implement your case for AD authentication.