sharing session between owin web form and regular web form - owin

I'm new to owin, OAuth, OpenID connect terminology. I'm trying learn as much as possible. I work with legacy .net web forms application. I have 2 webforms app. App_1 is more like a dashboard/landing page application. App_2 is the main application that has all the functionalities. I use Identity server 4 for authentication only(don't ask me why).
this is the flow:
1) user logs in using IS4 and lands in App_1.
2) all the metadata required for the authorization and business logic is stored in the session object in app_1 page load
3) the user then goes to app_2. it retrieves required data from the session object
previously there was no IS4 authentication. it was using forms authentication and the session concept was working fine. I just changed authentication to IS4 then the session object became null.
I tried various solutions suggested on the internet. It didn't work. I added this in app_1 startup.cs, but still it failed.
app.Use((context, next) =>
{
var httpContext = context.Get<HttpContextBase>(typeof(HttpContextBase).FullName);
httpContext.SetSessionStateBehavior(SessionStateBehavior.Required);
return next();
});
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationType = "Cookies",
ExpireTimeSpan = TimeSpan.FromMinutes(10),
CookieName="MyWebApp",
SlidingExpiration = true
});
Note: im also having the same machine key to in web.config of both the web apps.
Can anyone please tell me how to carry the session object between these 2 web apps?

Related

Cannot Logout from IdentityServer 4 through Azure SAML logout request

Context
I have a Service Provider (SP) based on IdentityServer 4 and Sustainsys.Saml2.AspNetCore2 that is configured to use Azure as an IdP (SAML2).
I also have a SPA with an api that connects to my SP (with oidp) to identify my user. The api then creates a JWT for my user to use.
I can login my user correctly.
Question
My issue comes with the logout. I want to use the logout url parameter of Azure to notify my SP about the logout. I manage to see the SAML Logout Request as a string when I configure an endpoint of mine but I can't exploit it and parsing it manually does't seem right.
Is there an existing endpoint that would come with my dependencies that I missed?
The goal here is to revoke all my user's application sessions (the apps to which my user is connected throug my SP).
Configuration
Idp configuration in the SP (called in Startup.cs).
The Saml2AuthModel comes from a config file.
public static AuthenticationBuilder AddSaml2Auth(this AuthenticationBuilder builder, Saml2AuthModel saml2AuthModel)
{
builder.AddSaml2(saml2AuthModel.Scheme, saml2AuthModel.DisplayName ?? saml2AuthModel.Scheme, options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.SignOutScheme = IdentityServerConstants.SignoutScheme;
options.SPOptions.EntityId = new EntityId(saml2AuthModel.ServiceProviderEntityId);
options.SPOptions.ModulePath = "/" + saml2AuthModel.ModulePath ?? saml2AuthModel.Scheme ?? options.SPOptions.ModulePath;
var idp = new IdentityProvider(
new EntityId(saml2AuthModel.IdentityProviderEntityId),
options.SPOptions
);
idp.MetadataLocation = saml2AuthModel.IdentityProviderMetadataLocation;
options.IdentityProviders.Add(idp);
});
return builder;
}
The Sustainsys.Saml2 library has support for single logout. To enable it, you need to set up a service signing key. The reason is that logout requests and responses should be signed. So the library doesn't expose the logout endpoints if it has no signing keys available.

How to validate request with token in .net core 2.2 api using Open Id Connect

I have a .net core 2.2 api setup and deployed to Azure. I have used OpenId Connect to handle the authentication using azure active directory single tenenat using the code below. And the Authorize decorator on my controller. Everything works and when I browse to my azure deployed api (myappname.azurewebsites.net) I get microsoft login prompt. I'm able to then login and view my route data.
services.AddAuthentication(auth =>
{
auth.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
auth.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
auth.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(opts =>
{
Configuration.GetSection("OpenIdConnect").Bind(opts);
opts.Events = new OpenIdConnectEvents
{
OnAuthorizationCodeReceived = ctx =>
{
return Task.CompletedTask;
}
};
opts.Scope.Add("openid");
opts.Scope.Add("profile");
opts.Scope.Add("access_as_user");
});
The problem is that when I have the Authorization turned on for my controller, I am not able to call it form my angular SPA client application. I have successfully configured MSAL and my api calls are passing a token. However, I get the following error:
https://login.microsoftonline.com/bit4f574-5968-4a40-049d-1c0dc2ca0513/oauth2/authorize?client_id=caor847f-dd19-4489-bef7-684803728c665&redirect_uri=https%3A%2F%2Fmyapi.azurewebsites.net%2Fsignin-oidc&response_type=code%20id_token&scope=openid%20profile%20user_access&response_mode=form_post&nonce=637373859487758409.MzhhYTAoeiudtMTdlNS00NzgxLWJjMTQtNzM1YWE3NsdlkelasdNGYxMmQtMjZmYS00YmI2LTgwY2UtNDEwMTNhMWNkN2Zi&state=CfDJ8KCu3Hr4UOhLjOspjLNEh0VtJd4GNXqwdibjSiZf7FpUJOL0EDlFso0g0s_iOZHDNbP2aiHVfdzqJSmHkesd-bMjP6ThYva6AfZBa8UZcnGcwgo2ldlg4Fx9vmNVDuSlvHyTlHkd8yNndslkgoyHtfM4RMXamq1wny1J39BZRRATn1RdAsgaLgKP_QkxLaDCwgvdzjp3dKls5UVQE1j7MD6bcKR__1-VmfVKhROn1coQh7OJrea6Jni4jdV7e0wv70TVprGtseJFg8fyHg3KKW14xeX2orlkgls5aLe1uG0c5ehlapFXBirBSgFU3uqOWw0_iLeJUbTL8-HPooixynQRWe1WoiLnQuFYUu7Lx-usdlglvM4WvLfAyTZ5uQY_KsOtr08MxWRlQ5HHVk8Moe1k_N_3BCz8sdkgowwZEKsGiKd_iwcXgzxmgg&x-client-SKU=ID_NETSTANDARD2_0&x-client-ver=5.3.0.0
How can I fix this? It seems like my api is redirecting the client request to the microsoft login, but I'm thinking the way this should work is that my api should validate the token in the request or the scopes and grant access without redirecting the request to a login.
The key point here is to separate SPA OAuth entirely from API OAuth, with the following behaviours:
SPA does the redirect handling and gets a token from the Authorization Server - this code is Javascript based and does not need a C# 'web back end'
API validates tokens via the token signing public key of the Authorization Server - this code is C# based, which is a good language for API development
Authorization Server is an out of the box component such as Azure AD, and you never code issuing of JWTs yourself (good to see you doing this already)
Many online articles I've seen mix up these concerns and can be very confusing to people who are new to OAuth technologies. So it's important to be clear about what you want from the end solution.
API OAUTH CODE
Your C# code should only need a few lines by default, starting by adding a package such as this one:
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.8" />
The code to configure the API's security would then look something like this. Note that there is nothing about OpenIdConnect / web behaviour here.
private void ConfigureOAuthTokenValidation(IServiceCollection services)
{
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => {
options.Authority = "https://login.microsoftonline.com/mytenantid";
options.Audience = "api://default";
});
REAL WORLD SAMPLE
My .Net Core API solution has some stuff that may be of interest. These samples are quite advanced but enable you to run a local API and SPA that work together.
.Net Core Blog Post
API Code
API Security Configuration Code
SPA Code
The goal is to get the best end result for both API and SPA, so perhaps the above links will help you think about what that is in your case.

GetExternalLoginInfoAsync always returns null when using Okta for authentication

I'm currently trying to get Okta to work with our MVC based application. Unfortunatly I am not the original designer or author of original code. However after some digging I have found that my predecessors work was based on the sustainsys example app "SampleOwinApplication" and this certainly seems to provided the functionality that we require. So I have based my query on this sample that can be obtained from https://github.com/Sustainsys/Saml2
This works with the sustainsys saml stub but now a genuine authentication provider (in this case Okta)
If I configure the application to use the sustainsys stub authentication provider and using Chrome with a plugin to view SAML tokens. I can see the SAML token come back and is presented to the call back as expected:
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
and when it runs loginInfo gets filed in and all works as expetced.
However when I change to configuration to use my Okta app, I get redirected to log in to Okta (as expecected) and I can see successful authentication and a SAML token comes back to my application (as seen in the Chrome plugin). However the above consumer for the callback ends up with a null value in loginInfo.
Further digging into the issue shows that in the Statup.Auth.cs there is the following code:
// 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.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
and then the Saml2 authentication is added
app.UseSaml2Authentication(CreateSaml2Options());
So it looks like cookie authentication is being used rather than saml2. If I check for cookies from the sustainsys site I can see them added to the browser and authentication works as expected. However, if I use Okta for authentication, no such cookies get set and the authentication fails.
Removing all the cookie authentication references results in:
A default value for SignInAsAuthenticationType was not found in IAppBuilder Properties. This can happen if your authentication middleware are added in the wrong order, or if one is missing.
So clearly this is required, shifting the call to app.UseSaml2Authentication(CreateSaml2Options()); before app.UseCookieAuthentication in the vain hope of it changing the priority and therefore picking up the SAML token fails and whilst the code runs authentication and the call to AuthenticationManager.GetExternalLoginInfoAsync, always results in a null value being returned regardless of the authentication provider.
So I either need to completely remove the cookie authentication so it is forced to use the saml packet, get Okta to set the necessary cookies or be able to parse the saml 2 token independently rather than relying on AuthenticationManager.GetExternalLoginInfoAsync to do the work.
Any clues/advice is appreciated
See the working configuration I am currently using successfully with Okta for a service provider initiated login:
AuthenticateResult.Succeeded is false with Okta and Sustainsys.SAML2
Unfortunately, it is still not working with an identity provider initiated login.
See: IdP Initiated Login with Sustainsys.SAML2 - AuthenticateResult Has No Information

Azure active directory v2.0 query for Web API integration on a SharePoint site

We have a SharePoint publishing site with anonymous access hosted on the internet. As per out latest requirements, we need to implement user login (AzureAD, Microsoft personal and work accounts, and more).
https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-flows
As per the documentation here, we want to implement this using Web API to get the secure information from the database. We are thinking about using MSAL.js file for user login and logout on the SharePoint and after getting a bearer token we can call the Web API for the additional data from our database.
Standalone Web APIs restriction: “You can use the v2.0 endpoint to build a Web API that is secured with OAuth 2.0. However, that Web API can receive tokens only from an application that has the same Application ID. You cannot access a Web API from a client that has a different Application ID. The client won't be able to request or obtain permissions to your Web API.”
How can we create two applications with same application ID at App Registration Portal? Or should we use the same application ID at SharePoint and Web API’s end?
There is no need to register two application, you only need to one register application. After you register the application, you can using the MSAL library below to get the token to call the web API:
<script class="pre">
var userAgentApplication = new Msal.UserAgentApplication("e5e5f2d3-4f6a-461d-b515-efd11d50c338", null, function (errorDes, token, error, tokenType) {
// this callback is called after loginRedirect OR acquireTokenRedirect (not used for loginPopup/aquireTokenPopup)
})
userAgentApplication.loginPopup(["user.read"]).then(function (token) {
var user = userAgentApplication.getUser();
console.log(token);
// signin successful
}, function (error) {
// handle error
});
</script>
And to protect the web API, you can use the same app and refer the code below:
public void ConfigureAuth(IAppBuilder app)
{
var tvps = new TokenValidationParameters
{
// The web app and the service are sharing the same clientId
ValidAudience = "e5e5f2d3-4f6a-461d-b515-efd11d50c338",
ValidateIssuer = false,
};
// NOTE: The usual WindowsAzureActiveDirectoryBearerAuthenticaitonMiddleware uses a
// metadata endpoint which is not supported by the v2.0 endpoint. Instead, this
// OpenIdConenctCachingSecurityTokenProvider can be used to fetch & use the OpenIdConnect
// metadata document.
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
AccessTokenFormat = new JwtFormat(tvps, new OpenIdConnectCachingSecurityTokenProvider("https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration")),
});
}

Azure Mobile Services custom authentication with MVC5

I've successfully setup a custom authentication process with Azure Mobile Services and my Windows Phone 8.1 app (following the guide here)
I'm now creating an MVC5 single page application (SPA) to manage the admin side of things for the system. I'm relatively new to MVC5 and just need a little help to get started with performing a login just like in my phone app?
Currently my phone app performs a login by
App.MobileService.CurrentUser = await AuthenticateAsync(this.textBox_email.Text, textBox_password.Password);
which does
private async Task<MobileServiceUser> AuthenticateAsync(string username, string password)
{
// Call the CustomLogin API and set the returned MobileServiceUser
// as the current user.
var user = await App.MobileService
.InvokeApiAsync<LoginRequest, MobileServiceUser>(
"CustomLogin", new LoginRequest()
{
UserName = username,
Password = password
});
return user;
}
this all works well so I guess the question is how do I do make a call to my customer authentication API in the same way in MVC5 and set the user context if successful?
Startup.Auth.cs:
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
// Enable the application to use a cookie to store information for the signed in user
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login")
});
// Use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Uncomment the following lines to enable logging in with third party login providers
//app.UseMicrosoftAccountAuthentication(
// clientId: "",
// clientSecret: "");
//app.UseTwitterAuthentication(
// consumerKey: "",
// consumerSecret: "");
//app.UseFacebookAuthentication(
// appId: "",
// appSecret: "");
//app.UseGoogleAuthentication();
}
Let me know if I'm missing any info or detail.
Thanks!
Unfortunately this is not easy to do in Mobile Services. While you could achieve login using the Mobile Services HTML/JS SDK (served in an MVC view), this will not set the user context.
Because of Mobile Services incompatibility with MVC (addressed in the new Mobile Apps product), you won't be able to rely on that SDK. Unfortunately that means writing custom middleware/filters.
The easiest solution is probably to package your username/password validation and storage logic into code that can be shared by your Mobile Services project and your MVC project. The MVC project would need to take the validated user and issue a session cookie which is then read by a custom middleware or filter.
Writing an AuthorizationFilter implementation will be much easier than an OWIN middleware, so I would recommend that approach. Check if the cookie is present and valid, and if so set the user context.

Resources