I'm trying to add authentication option to support Azure AD in an asp.net application. The web.config file has some options:
<add key="ida:FederationMetadataLocation" value="https://login.microsoftonline.com/common/FederationMetadata/2007-06/FederationMetadata.xml" />
<add key="ida:Realm" value="..." />
<add key="ida:LogoutReply" value="..." />
<add key="ida:OwinWsFederationEnabled" value="true" />
In addition, the authentication is configured in code like this:
public partial class Startup{
public void ConfigureAuth(IAppBuilder app){
app.SetDefaultSignInAsAuthenticationType(DefaultAuthenticationTypes.ApplicationCookie);
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString(...),
LogoutPath = new PathString(...),
});
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "ExternalCookie",
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Passive,
});
var authenticationOptions = new WsFederationAuthenticationOptions()
{
AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType,
MetadataAddress = metadata,
Wtrealm = realm,
Wreply = reply,
Caption = caption,
SignOutWreply = logoutreply,
TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuer = false
}
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Passive,
};
app.UseWsFederationAuthentication(authenticationOptions);
}
}
This kind of works, because it redirects me to azure login and back to the app login path. But there, when all is supposed to be ok, I'm not getting any information about the newly logged in user.
var oid = GetClaim("http://schemas.microsoft.com/identity/claims/objectidentifier");
with this or any other claim type, for that matter. All the claims are returning null-values.
The app is registered to my Azure AD directory as multitenant.
If I set the metadatalocation to be the tenant specific metadata, the AAD won't redirect me back to the application as the azure ad user is from another tenant.
What am I missing here to get the user claims from Azure AD?
Related
the below is working code.
But I need to implement the solution without sing coockies (UseCookieAuthentication).
After Azure authentication, when we check the AuthenticationTyoe in Identity it shown as Cookies, which i need to avoid. any suggetions/other implementations
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
// Sets the ClientId, authority, RedirectUri as obtained from web.config
ClientId = clientId,
Authority = authority,
RedirectUri = redirectUri,
// PostLogoutRedirectUri is the page that users will be redirected to after sign-out. In this case, it is using the home page
PostLogoutRedirectUri = redirectUri,
Scope = OpenIdConnectScope.OpenIdProfile,
// ResponseType is set to request the code id_token - which contains basic information about the signed-in user
ResponseType = OpenIdConnectResponseType.CodeIdToken,
// OpenIdConnectAuthenticationNotifications configures OWIN to send notification of failed authentications to OnAuthenticationFailed method
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed
}
}
);
}
I am trying to get gain an Access Token after receiving the authorisation token via postback from Azure.
I simply serve the Home view where there is a button to logon to Azure. Initially i followed this post: https://learn.microsoft.com/en-us/azure/active-directory/develop/tutorial-v2-asp-webapp but then tried to modify the code to follow the Authorisation Code Flow.
Strangely it was working nicely (after googling for days) when hosted on IIS Express but when moving to local IIS I am only able to complete half the flow. I wonder if it's significant that my app is running as an Application on IIS not as a website? The address is https://localhost/testapp. My site on IIS runs on port 80 and 443 as standard. IIS is as per (apologies for the heavy redaction):
In essence the code redemption is not taking place.
Here is my owin Startup.cs:
string clientId = ConfigurationManager.AppSettings["ClientId"];
string clientSecret = ConfigurationManager.AppSettings["ClientSecret"];
string redirectUri = ConfigurationManager.AppSettings["RedirectUri"];
static string tenant = ConfigurationManager.AppSettings["Tenant"];
string authority = string.Format(System.Globalization.CultureInfo.InvariantCulture, ConfigurationManager.AppSettings["Authority"], tenant);
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
// Sets the ClientId, authority, RedirectUri as obtained from web.config
ClientId = clientId,
ClientSecret = clientSecret,
Authority = authority,
RedirectUri = redirectUri, //struggling to see the difference between RedirectUri and CallbackPath
CallbackPath = new PathString("/home/"), // do i need this as well?
PostLogoutRedirectUri = redirectUri,
Scope = OpenIdConnectScope.OpenIdProfile,
RedeemCode = true,
ResponseMode = OpenIdConnectResponseMode.FormPost, // do i need this?
SaveTokens = true, // do i need this?
UsePkce = true, // default is true
ResponseType = OpenIdConnectResponseType.Code,
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = false,
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed,
},
}
);
}
My home controller is simply:
public class HomeController : Controller
{
[AllowAnonymous]
public ActionResult Index()
{
return View();
}
[AllowAnonymous]
public void SignIn()
{
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties { RedirectUri = "/" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
/// <summary>
/// Send an OpenID Connect sign-out request.
/// </summary>
[AllowAnonymous]
public void SignOut()
{
HttpContext.GetOwinContext().Authentication.SignOut(
OpenIdConnectAuthenticationDefaults.AuthenticationType,
CookieAuthenticationDefaults.AuthenticationType);
}
}
web.config:
<add key="ClientId" value="xxxxxxxxxxxx" />
<add key="ClientSecret" value="xxxxxxxxxxxx" />
<add key="redirectUri" value="https://localhost/testapp/home/" />
<add key="Tenant" value="xxxxxxxxxxxx" />
<add key="Authority" value="https://login.microsoftonline.com/{0}/v2.0" />
On Azure the Redirect URI is:
this is the trace from fiddler:
You can see from the trace that there is not further call to the token endpoint even though I believe I have the correct configuration for OWIN to make the call automatically?
I am really stumped and I would appreciate some help please.
I have two MVC applications AppA and AppB, and implemented Azure AD authentication for login.
I am able to sign-in successfully to both applications.
But the issue is, after I login to AppA and then to AppB, after sometime when I return back to AppA I am facing the issue where user has been logged out, and it again redirects to login screen (in AppA).
After I login to AppA (second time) and go back to AppB (user in AppB is logged out).
Client IDs are different ; TenandID is same. Both apps are hosted in same server.
Startup file:
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
SlidingExpiration = true,
Provider = new CookieAuthenticationProvider
{
OnResponseSignIn = context =>
{
context.Properties.AllowRefresh = true;
context.Properties.ExpiresUtc = DateTimeOffset.UtcNow.AddDays(1);
},
OnValidateIdentity = MyCookieValidateIdentity
},
ExpireTimeSpan = TimeSpan.FromDays(2)
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = appId,
//CookieManager=new SameSiteCookieManager(new SystemWebCookieManager()),
Authority = "https://login.microsoftonline.com/xxxxxx/v2.0",
Scope = $"openid email profile offline_access {graphScopes}",
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = (context) =>
{
context.ProtocolMessage.DomainHint = "xyz.com";
return Task.FromResult(0);
},
// SecurityTokenValidated = OnSecurityTokenValidated,
AuthenticationFailed = OnAuthenticationFailedAsync,
AuthorizationCodeReceived = OnAuthorizationCodeReceivedAsync
}
}
);
}
actionContext.RequestContext.Principal.Identity.IsAuthenticated is returning False
I am assuming it has to do something with the cookie. Can someone please help resolve this ?
Edit:
Debugged further and found:
Initially if the cookies for AppA are set as:
.AspNet.Cookies = A_abc123 ; ASP.NET_SessionId = A_def456
And for AppB .AspNet.Cookies = B_mno123 ; ASP.NET_SessionId = B_pqr456
Then after I click any link in AppA, the cookie's values are updated with AppB's cookies, i.e. .AspNet.Cookies = B_mno123 ; ASP.NET_SessionId = B_pqr456
.AspNet.Cookies ASP.NET_SessionId
AppA A_abc123 A_def456
AppB B_mno123 B_pqr456
AppA B_mno123 B_pqr456
One thing that you need to do is to configure the Data Protection API so that both services uses the same cookie protection key. Out of the box each service creates its own unique key, and a cookie from one service is not valid in a different service.
I also did a blog post about the data protection API here.
See
How to: Use Data Protection
Get started with the Data Protection APIs in ASP.NET Core
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
//AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,// DefaultAuthenticationTypes.ApplicationCookie,
CookieName = ".AspNet.AppA.Cookies",
SlidingExpiration = true,
CookieManager = new SystemWebCookieManager(),
Provider = new CookieAuthenticationProvider
{
OnResponseSignIn = context =>
{
context.Properties.AllowRefresh = true;
context.Properties.ExpiresUtc = DateTimeOffset.UtcNow.AddDays(1);
},
},
ExpireTimeSpan = TimeSpan.FromDays(2)
});
//... code removed for brevity //
}
The Default Cookie Name set by the application was: .AspNet.Cookies
And when I modified the default cookie name, the issue got resolved. Each application was generating its own cookiename and hence the other application was not signing out the user.
Trying to fix this Azure Active Directory issue. I have an ASP.Net 4.7 website. It correctly takes me to the SSO page and confirms my identity. However, upon taking me back to my site, I get the message:
AADSTS50011: The reply URL specified in the request does not match the reply URLs configured for the application: 'MyApplication-ClientID'.
It makes sense that the AD App Registration's | Authentication | Redirect URI does not match what I am sending it. However, as near as I can tell, they do.
Here is my code in the Startup.cs file:
public void Configuration(IAppBuilder app) {
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions {
ClientId = clientId, // MyApplication-ClientID
Authority = authority, // https://login.microsoftonline.com/MyDirectory-TenantID/v2.0
RedirectUri = redirectUri, // https://MySiteName.azurewebsites.net
PostLogoutRedirectUri = redirectUri,
Scope = OpenIdConnectScope.OpenIdProfile,
ResponseType = OpenIdConnectResponseType.IdToken,
TokenValidationParameters = new TokenValidationParameters() {
ValidateIssuer = false
},
Notifications = new OpenIdConnectAuthenticationNotifications {
AuthenticationFailed = OnAuthenticationFailed
}
}
);
}
Here are the settings in my Web.config file:
<add key="ClientId" value="MyApplication-ClientID" />
<add key="Tenant" value="common" />
<add key="Authority" value="https://login.microsoftonline.com/MyDirectory-TenantID/v2.0" />
<add key="RedirectUri" value="https://MySiteName.azurewebsites.net" />
Both the Redirect URI and the Configs RedirectUri are: https://MySiteName.azurewebsites.net
Here is the initial request when I am asked to select an account when trying to log in:
https://login.microsoftonline.com/MyDirectory-TenantID/oauth2/v2.0/authorize
?response_type=code+id_token
&redirect_uri=https%3A%2F%2FMySiteName.azurewebsites.net%2F.auth%2Flogin%2Faad%2Fcallback
&client_id=MyApplication-ClientID
&scope=openid+profile+email
&response_mode=form_post
&nonce=3f63a75d79af449082801c5183d5fbdb_20200710145808
&state=redir%3D%252F
So I see the redirect_uri seems to add the /.auth/login/aad/callback to the end of what I told it to be. So I updated the AD App Registration's | Authentication | Redirect URI to match and instead of the above error I get the following error:
The page cannot be displayed because an internal server error has occurred.
I am just at a loss here trying to figure out what is mismatched or perhaps just missing.
The OWIN middleware stuff to integrate third-party logins to your ASP.NET app is very cool, but I can't seem to figure out how to tear it out from the new ID framework that replaces the crappy Membership API. I'm not interested in persisting the resulting claims and user info in that EF-based data persistence, I just want the claims info so I can apply it to my own user accounts in existing projects. I don't want to adopt the new ID framework just to take advantage of this stuff.
I've been browsing the code on CodePlex, but there's a whole lot of static magic. Can you offer any suggestions?
Use the following code to setup OWIN security middlewares:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Application",
AuthenticationMode = AuthenticationMode.Passive,
LoginPath = new PathString("/Login"),
LogoutPath = new PathString("/Logout"),
});
app.SetDefaultSignInAsAuthenticationType("External");
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "External",
AuthenticationMode = AuthenticationMode.Passive,
CookieName = CookieAuthenticationDefaults.CookiePrefix + "External",
ExpireTimeSpan = TimeSpan.FromMinutes(5),
});
app.UseGoogleAuthentication();
The code above sets up application cookie, external cookie and Google external login middlewares. External login middleware will convert external user login data as identity and set it to external cookie middleware. In your app, you need to get external cookie identity and convert it to external login data, then you can check it with your db user.
Here are some sample code.
Sign in with application cookie:
var authentication = System.Web.HttpContext.Current.GetOwinContext().Authentication;
var identity = new ClaimsIdentity("Application");
identity.AddClaim(new Claim(ClaimTypes.Name, "<user name>"));
authentication.AuthenticationResponseGrant = new AuthenticationResponseGrant(identity, new AuthenticationProperties() {
IsPersistent = false
});
Get application cookie identity:
var identity = System.Web.HttpContext.Current.User.Identity as ClaimsIdentity;
Get external cookie identity (Google):
var authentication = System.Web.HttpContext.Current.GetOwinContext().Authentication;
var result = await authentication.AuthenticateAsync("External");
var externalIdentity = result.Identity;
Extract external login data from identity:
public static ExternalLoginData FromIdentity(ClaimsIdentity identity)
{
if (identity == null)
{
return null;
}
Claim providerKeyClaim = identity.FindFirst(ClaimTypes.NameIdentifier);
if (providerKeyClaim == null || String.IsNullOrEmpty(providerKeyClaim.Issuer)
|| String.IsNullOrEmpty(providerKeyClaim.Value))
{
return null;
}
if (providerKeyClaim.Issuer == ClaimsIdentity.DefaultIssuer)
{
return null;
}
return new ExternalLoginData
{
LoginProvider = providerKeyClaim.Issuer,
ProviderKey = providerKeyClaim.Value,
UserName = identity.FindFirstValue(ClaimTypes.Name)
};
}