Restrict user from opening new tab in MVC application - asp.net-mvc-4

How to restrict a user from opening a new tab in mvc application, we are using azure active directory authentication so by default it uses cookie & we can't disable cookie.
In order to store data we are using session to store class details and its persisted throughout the application however a new tab creates an issue as the session details are getting shared, what we can do to resolve this issue any code link will surely help.
code using AAD
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType); // Authentication type is cookies
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = (context) =>
{
X509Certificate2 cert = null;
X509Store store = new X509Store(StoreLocation.CurrentUser);
// Here the code all about certification & access token
return Task.FromResult(0);
}
}
});
}

Related

How do I avoid using a client secret or certificate for Blazor Server when using MSAL?

When using Blazor Server and the MSAL library you must provide either a client secret or a client certificate. Here is what a Blazor Server project uses to setup the authentication out of the box.
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"));
However in the Blazor WASM (Or Blazor Client) project they set things up this way
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
});
Obviously this method doesn't require a Client Secret for security reasons. The AddMsalAuthentication() method that it uses is only found in the WebAssembly MSAL library.
We don't use a key vault as our company doesn't want to pay for one at this time. So we have to manually update all the client secrets every 24 months. To avoid this we want to try to implement a public client flow in our Blazor Server apps. We already have a bunch of them so we don't want to have to move them to WebAssembly.
I did try to implement using a public client builder manually but the .AddMicrosoftIdentityWebApp() portion would still require a client secret to allow the first login.
In Blazor Server is there a way to implement the same behavior as the WebAssembly authentication, in other words, can we avoid using Client Secrets and Certificates?
I would recommend to do as the following to be able to retrieve access token without client secret.
Statup class:
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(o => {
o.UsePkce = true;
o.ClientId = "clientId";
o.TenantId = "tenantId";
o.Domain = "domain.onmicrosoft.com";
o.Instance = "https://login.microsoftonline.com";
o.CallbackPath = "/signin-oidc";
o.Scope.Add("scopeApi1");
o.Scope.Add("offline_access");
o.ResponseType = "code";
var defaultBackChannel = new HttpClient(); defaultBackChannel.DefaultRequestHeaders.Add("Origin", "thisismyapp");
o.Backchannel = defaultBackChannel;
});
services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
options.SaveTokens = true;
options.Events.OnTokenValidated = async context =>
{
// Here you will be able to see the accesstoken.
var accessToken = context.TokenEndpointResponse.AccessToken;
var refreshToken = context.TokenEndpointResponse.RefreshToken;
};
});
In your case change the "service" to "builder.Services".
Then in the _Host.cshtml
var accessToken = await HttpContext.GetTokenAsync("access_token");
var refreshToken = await HttpContext.GetTokenAsync("refresh_token");
<component type="typeof(App)" param-AccessToken="#accessToken" param-RefreshToken="#refreshToken" render-mode="Server" />
In App.razor:
[Parameter]
public string AccessToken { get; set; }
[Parameter]
public string RefreshToken { get; set; }
From here you will be able to do what ever you want with the accesstoken..
Some issue i have been facing with this, is how to get a new accesstoken with the refreshtoken without client secret. If you figure it out, let me know.

Azure AD Authentication and authorization

I am developing Asp.net MVC project, This app authenticating form Azure AD but problem with role based authorization, action not authorized base in the role group. I put my code here, please review and help me. Contact action not authorizing, I created Operator1 group of security type and assigned to user
public partial class Startup {
private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
private static string appKey = ConfigurationManager.AppSettings["ida:ClientSecret"];
private static string aadInstance = EnsureTrailingSlash(ConfigurationManager.AppSettings["ida:AADInstance"]);
private static string tenantId = ConfigurationManager.AppSettings["ida:TenantId"];
private static string postLogoutRedirectUri = ConfigurationManager.AppSettings["ida:PostLogoutRedirectUri"];
public static readonly string Authority = aadInstance + tenantId;
// This is the resource ID of the AAD Graph API. We'll need this to request a token to call the Graph API.
string graphResourceId = "https://graph.windows.net";
public void ConfigureAuth(IAppBuilder app) {
ApplicationDbContext db = new ApplicationDbContext();
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions {
ClientId = clientId,
Authority = Authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
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(clientId, appKey);
string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
AuthenticationContext authContext = new AuthenticationContext(Authority, new ADALTokenCache(signedInUserID));
AuthenticationResult result = authContext.AcquireTokenByAuthorizationCodeAsync(
code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceId).Result;
return Task.FromResult(0);
}
}
});
}
}
Look into this sample below which helps you add authorization using app roles & roles claims to an ASP.NET Core web app that's signs-in users with the Microsoft identity platform.
Learn more here:
https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/master/5-WebApp-AuthZ/5-1-Roles
My code is working for me just I was needed to register app as Enterprise Application.

How to automatically login Okta user with active login session to .NET 4.5 Web Forms app

I've got a .NET 4.5 Web Forms app with Okta authentication on top. The authentication setup seems to be working fine; I can login and logout and get my Okta user info/claims from the context variable.
What I'd like to do is detect on page load whether a user already has an active Okta session in their browser and then log them into the application. Or if they don't have a session do nothing and stay on the application page.
Making a challenge call to the authentication manager
HttpContext.Current.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties { RedirectUri = "/Login.aspx" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
almost does what I want. If the user has an active session it'll do some redirects and log them in. But if they're not logged in they get sent to, and left on, the Okta login page. Which is not what I want.
I thought I would be able to access some cookies that Okta sets when a user logs in via an Okta page, but when checking through the browser debugger and checking Request.Cookies they don't seem to be available at that stage. And the context doesn't have access to the user info either.
edit: Also, if it helps, this is what my Startup.cs looks like
using System;
using System.Threading.Tasks;
using Microsoft.Owin;
using Owin;
using IdentityModel.Client;
using Microsoft.AspNet.Identity;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OpenIdConnect;
using System.Collections.Generic;
using System.Configuration;
using System.Security.Claims;
[assembly: OwinStartup(typeof(WebApplication.Startup))]
namespace WebApplication
{
public class Startup
{
// These values are stored in Web.config. Make sure you update them!
private readonly string _clientId = ConfigurationManager.AppSettings["okta:ClientId"];
private readonly string _redirectUri = ConfigurationManager.AppSettings["okta:RedirectUri"];
private readonly string _authority = ConfigurationManager.AppSettings["okta:OrgUri"];
private readonly string _clientSecret = ConfigurationManager.AppSettings["okta:ClientSecret"];
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
public void ConfigureAuth(IAppBuilder app)
{
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = _clientId,
ClientSecret = _clientSecret,
Authority = _authority,
RedirectUri = _redirectUri,
ResponseType = OpenIdConnectResponseType.CodeIdToken,
Scope = "openid profile email offline_access",
TokenValidationParameters = new TokenValidationParameters { NameClaimType = "name" },
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async n =>
{
// Exchange code for access and ID tokens
var tokenClient = new TokenClient($"{_authority}/v1/token", _clientId, _clientSecret);
var tokenResponse = await tokenClient.RequestAuthorizationCodeAsync(n.Code, _redirectUri);
if (tokenResponse.IsError)
{
throw new Exception(tokenResponse.Error);
}
var userInfoClient = new UserInfoClient($"{_authority}/v1/userinfo");
var userInfoResponse = await userInfoClient.GetAsync(tokenResponse.AccessToken);
var claims = new List<Claim>(userInfoResponse.Claims)
{
new Claim("code", n.Code),
new Claim("id_token", tokenResponse.IdentityToken),
new Claim("refresh_token", tokenResponse.RefreshToken),
new Claim("access_token", tokenResponse.AccessToken)
};
n.AuthenticationTicket.Identity.AddClaims(claims);
},
},
});
}
}
}

Azure AD Open Id Connect in .Net MVC and Angular 6

I am making two apps which are authenticating from an azure ad, these both apps have different client id and within the same tenant, but here I am facing a problem that these both signing in with one click but logout behavior is not same, I have to log out both apps separately
Here is my .Net MVC authentication code
public class Startup
{
string clientId = System.Configuration.ConfigurationManager.AppSettings["ida:ClientId"];
// RedirectUri is the URL where the user will be redirected to after they sign in.
string redirectUri = System.Configuration.ConfigurationManager.AppSettings["ida:RedirectUri"];
// Tenant is the tenant ID (e.g. contoso.onmicrosoft.com, or 'common' for multi-tenant)
static string tenant = System.Configuration.ConfigurationManager.AppSettings["ida:Tenant"];
// Authority is the URL for authority, composed by Azure Active Directory v2 endpoint and the tenant name (e.g. https://login.microsoftonline.com/contoso.onmicrosoft.com/v2.0)
string authority = String.Format(System.Globalization.CultureInfo.InvariantCulture, System.Configuration.ConfigurationManager.AppSettings["ida:Authority"], tenant);
/// <summary>
/// Configure OWIN to use OpenIdConnect
/// </summary>
/// <param name="app"></param>
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 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
}
}
);
}
/// <summary>
/// Handle failed authentication requests by redirecting the user to the home page with an error in the query string
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
{
context.HandleResponse();
context.Response.Redirect("/?errormessage=" + context.Exception.Message);
return Task.FromResult(0);
}
}
and what i have to do if i want to login with OpenIdConnectAuthenticationDefaults as below, its showing me no client_id error
app.SetDefaultSignInAsAuthenticationType(OpenIdConnectAuthenticationDefaults.AuthenticationType);
var options = new OpenIdConnectAuthenticationOptions();
options.Configuration = new OpenIdConnectConfiguration
{
AuthorizationEndpoint = authority + "/authorize,
JwksUri = authority + "/.well-known/openid-configuration",
TokenEndpoint = authority + "/token",
UserInfoEndpoint = authority,
Issuer = authority,
};
app.UseOpenIdConnectAuthentication(options);
at last this is my angular auth code
configureAuth() {
this._oauthService.configure(environment.config.oid_auth_config);
this._oauthService.setStorage(sessionStorage);
// // This method just tries to parse the token(s) within the url when
// // the auth-server redirects the user back to the web-app
// // It dosn't send the user the the login page
this._oauthService.tryLogin({
onTokenReceived: context => {
//
// Output just for purpose of demonstration
// Don't try this at home ... ;-)
//
alert('token recieved');
console.log('logged in');
console.log(context);
}
})
.catch(err => {
this._sessionService.clearSession();
location.href = 'http://localhost:54503/';
console.error(err);
})
.then(() => {
if (!this._oauthService.hasValidAccessToken()) {
this._oauthService.initImplicitFlow();
} else {
this._authService.getFirstUser().subscribe((_user: any) => {
this._sessionService.createSession(_user);
this._authService.updateLoginStatus();
this._router.navigate(['change-viewer']);
});
}
// if (!this._oauthService.hasValidAccessToken()) {
// this._oauthService.initImplicitFlow();
// }
});
}
Please help me out with this issue.

Thinktecture Identity server v3 Google provider

I am getting issue while integration external provider i.e Google with Thinktecture identity server v3 .I am getting following error: "The client application is not known or is not authorized."
Do any one have any idea about this error.
#Whoever, it looks like you have a mismatch on the RedirectUri values in the client and server.
The RedirectUri property in the client startup defines the URI that will be called called after authentication by the identity server. The RedirectUris in the server config defines the listed of allowed URIs that can request authentication. The client startup RedirectUri must therefore be included in the server's RedirectUris list.
Looks like your client's RedirectUri is currently pointing at the server's URI. Is your client running on port 46289? If so, try changing the value of RedirectUri property in the client startup to https://localhost:46289. You might also want to try modifying the server's redirectUris value to use https rather than http, assuming that your client really is accessible over https.
Server client store:
public static IEnumerable<Client> Get()
{
return new[] {
new Client {
Enabled = true,
ClientName = "MVC Client",
ClientId = "mvc",
Flow = Flows.Implicit,
RedirectUris = new List<string>{
"https://localhost:46289/" // client home url
Client startup:
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
app.UseCookieAuthentication(new CookieAuthenticationOptions {
AuthenticationType = "Cookies"
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions {
Authority = "https://localhost:44300/identity",
ClientId = "mvc",
RedirectUri = "https://localhost:46289/", //must be in server's Client.RedirectUris
ResponseType = "id_token",
SignInAsAuthenticationType = "Cookies"
});
I had this problem. The RedirectUris entry in the servers almost matched the RedirectUri in the client Startup.Configuration; all but for the trailing slash.
https://localhost:46289/
is not the same as
https://localhost:46289
When I added the slash, my login page appeared.
I've been working through the same issue but just authenticating against Identity Server (Google is next to tackle on my list). I saw the issue because the Scopes for the client weren't setup on both the Mvc and Server. To resolve the issue I added the Scopes into the Startup class (of the Mvc client) as follows:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Authority = "https://localhost:44301",
Scope = "openid profile email roles",
ClientId = "mvc",
RedirectUri = "https://localhost:44300/",
ResponseType = "id_token",
SignInAsAuthenticationType = "Cookies"
});
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies"
});
}
}
..and also in the server's list of clients:
public static class Clients
{
public static IEnumerable<Client> Get()
{
return new[]
{
new Client
{
Enabled = true,
ClientName = "MVC Client",
ClientId = "mvc",
Flow = Flows.Implicit,
RequireConsent = true,
RedirectUris = new List<string>
{
"https://localhost:44300/"
},
PostLogoutRedirectUris = new List<string>
{
"https://localhost:44300/"
},
AllowedScopes = new List<string> {
Constants.StandardScopes.OpenId,
Constants.StandardScopes.Profile,
Constants.StandardScopes.Email,
Constants.StandardScopes.Roles
}
}
};
}
}
In relation to the OP's question with Google, it may be worth checking your scopes correlate with those supported by your app setup within the Google Developer Console too. There's a good SO post on supported scopes at Where can I find a list of scopes for Google's OAuth 2.0 API?
Hope that helps :)
Looks like client(application in which you want to have a possibility to log in with Google) is not registered in the client store. Could you, please, show your Startup Configuration?
In my case, I was not careful and was changing the values in Startup.cs under UseOpenIdConnectAuthentication (which are what the integrated web application uses to connect to itself) when I should have been changing the values in Clients.Get(), which are the allowed clients that the server has configured.
Once I fixed those, I was able to separate client and server into two applications with only some NuGet packages and UseCookieAuthentication/UseOpenIdConnectAuthentication in the client application.
You can get the error if the client is not enabled, redirect uri does not match one in the list (uses non case-sensitive exact match), if the scopes requested are not in the allowed scope list, if the flow requested does not match what is allowed (you can only have one per client) and/or if the client ids do not match.