I am trying to develop a MVC ASP.NET C# Application that can support both Azure AD Authentication and Forms authentication.
I've read about it and came to the following conclusion:
I have a login-form for Forms Auth and a button which redirects me to Azure AD Login.
After I login in AD, it auto redirects me to
http://localhost/login.aspx?ReturnUrl=%2f.
Using following code:
Startup.cs
public partial class Startup
{
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,
RedirectUri = postLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = context =>
{
context.HandleResponse();
context.Response.Redirect("/Error?message=" + context.Exception.Message);
return Task.FromResult(0);
}
}
});
}
}
AccountController.cs
public void SignIn()
{
// Send an OpenID Connect sign-in request.
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/" }, OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
public void SignOut()
{
// Send an OpenID Connect sign-out request.
HttpContext.GetOwinContext().Authentication.SignOut(
OpenIdConnectAuthenticationDefaults.AuthenticationType, CookieAuthenticationDefaults.AuthenticationType);
}
public void EndSession()
{
// If AAD sends a single sign-out message to the app, end the user's session, but don't redirect to AAD for sign out.
HttpContext.GetOwinContext().Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType);
}
My question is why does it redirects me to http://localhost/login.aspx?ReturnUrl=%2f, giving that the app is a MVC and there is no aspx in my project.
By default, the login URL for Forms authentication is Login.aspx. You can specify the login URL for Windows Forms authentication in the web.config:
<authentication mode="Forms">
<forms loginUrl="/account/signin" defaultUrl="/" />
</authentication>
More info: FormsAuthentication.LoginUrl Property
Related
Implemented Server Client Authorization based on recent Velusia example taken from here: openiddict-samples
all ASP NET.CORE clients work fine, but unfortunately I have to implement token authorization flow for some old ASP NET 4.8 Web form applications.
Added following OWIN startup class
public class Startup
{
// These values are stored in Web.config. Make sure you update them!
private readonly string _clientId = ConfigurationManager.AppSettings["ClientId"];
private readonly string _redirectUri = ConfigurationManager.AppSettings["RedirectUri"];
private readonly string _authority = ConfigurationManager.AppSettings["AuthUrl"];
private readonly string _clientSecret = ConfigurationManager.AppSettings["ClientSecret"];
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = _clientId,
ClientSecret = _clientSecret,
Authority = _authority,
RedirectUri = _redirectUri,
RequireHttpsMetadata = false,
ResponseType = OpenIdConnectResponseType.Code,
Scope = $"{OpenIdConnectScope.OpenId} profile {OpenIdConnectScope.Email} roles",
TokenValidationParameters = new TokenValidationParameters { NameClaimType = "name" , RoleClaimType = "role"},
CallbackPath = new PathString("//"), //signin-oidc
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async n =>
{
var client = new HttpClient();
var tokenResponse = await client.RequestAuthorizationCodeTokenAsync(new AuthorizationCodeTokenRequest
{
Address = _authority + "/connect/token",
ClientId = _clientId,
ClientSecret = _clientSecret,
Code = n.Code,
RedirectUri = _redirectUri
});
if (!tokenResponse.IsError)
{
var claims = new List<Claim>()
{
new Claim("id_token", tokenResponse.IdentityToken),
new Claim("access_token", tokenResponse.AccessToken)
};
n.AuthenticationTicket.Identity.AddClaims(claims);
}
},
},
});
}
}
behind the login button following code:
if (!Request.IsAuthenticated)
{
HttpContext.Current.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties { RedirectUri = "/" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
initially it starts fine, click on the login button and request goes to the server, I can see server login page. Once the password is entered request comes back to the client and opens following page: https://localhost/signin-oidc ... and I never get AuthorizationCodeReceived event triggered!
Anybody tried something similar?
I have an ASP.NET MVC application that is registered in Azure Active Directory. I am adding a new feature to the app which will require a list of all users from AAD. By putting together some code snippets I got from various MSDN docs, this is what my method to get the users looks like so far:
public async Task GetUsers()
{
string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
string tenant = ConfigurationManager.AppSettings["ida:Tenant"];
string clientSecret = ConfigurationManager.AppSettings["ida:ClientSecret"];
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenant)
.WithClientSecret(clientSecret)
.Build();
ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var users = await graphClient.Users.Request().GetAsync();
}
However, when I run the application, I get Original exception: AADSTS7000215: Invalid client secret is provided. I double checked to make sure that the client secret that I added to web.config matches what's shown in AAD, and that the client secret hasn't expired. I even deleted the client secret and created a new one, but that didn't fix the problem either. I also ensured that the permission User.Read.All of type Application has been granted for this app. What could be causing this error, and what could be done to resolve it? Also, I'm wondering if there's a simpler way to get a list of users given that I already have authentication set up for this app using Owin.IAppBuilder. Here's what I have in my Startup.cs file:
public class Startup
{
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 Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
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 = ctx => {
ctx.HandleResponse();
ctx.Response.Redirect("/Error/messages" + ctx.Exception.Message);
return Task.FromResult(0);
}
} });
}
}
It works for me this way (using WithAuthority), I did not need any additional scopes:
string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
string tenantId = ConfigurationManager.AppSettings["ida:TenantId"];
string clientSecret = ConfigurationManager.AppSettings["ida:ClientSecret"];
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithAuthority(new Uri($"{aadInstance}{tenantId}/"))
.WithClientSecret(clientSecret)
.Build();
and then...
return await graphClient.Users
.Request()
.GetAsync();
I have tried various authentication scenarios of Azure Active Directory across internet. All examples are focused only on Authorization by Authentication. I was looking for Authorizing the user based on Roles from my AAD App Registration.
Auth() Scenarios,
For example,
..\Controller\ArtistController.cs:
public class ArtistController : ApiController
{
[Authorize(Roles = "Admin, InternalAdmin")]
public void Post(ArtistModel model)
{
// Do admin stuff here...
}
}
..\App_Start\Startup.Auth.cs [Not working]:
public void ConfigureAuth(IAppBuilder app)
{
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
TokenValidationParameters = new TokenValidationParameters
{
SaveSigninToken = true,
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"],
RoleClaimType = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role"
}
});
}
..\App_Start\Startup.Auth.cs [Working]:
public void ConfigureAuth(IAppBuilder app)
{
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = ConfigHelper.ClientId,
Authority = ConfigHelper.Authority,
RedirectUri = "<<Home_Url>>",
PostLogoutRedirectUri = ConfigHelper.PostLogoutRedirectUri,
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
NameClaimType = "upn",
RoleClaimType = "roles", // The claim in the Jwt token where App roles are provided.
},
Notifications = new OpenIdConnectAuthenticationNotifications()
{
AuthenticationFailed = context =>
{
context.HandleResponse();
context.Response.Redirect("/Error/ShowError?signIn=true&errorMessage=" + context.Exception.Message);
return System.Threading.Tasks.Task.FromResult(0);
}
}
});
}
I understand that OWIN can wire any middleware to handle incoming http requests. Auth Middlewares like OpenId, WindowsBearerToken,...
Is UseOpenIdConnectAuthentication() the only correct middleware to authorize web resources by roles over UseWindowsAzureActiveDirectoryBearerAuthentication() based on this example?
Please suggest.
Yes, OpenID is the only middleware that will work for this. There is no alternative at this point to OpenID Connect.
I found the best way to set the roles is to add these roles in the manifest and then hard code the logic to give different permissions to different users.
This is the best sample that I have found for this so far. You just need to add the connection string to Azure SQL for it to work. https://github.com/Azure-Samples/active-directory-dotnet-webapp-roleclaims
I have an asp.net MVC and webapi2 application.I want to set OrderId in session and send User to bank website to pay the order and when the bank website will return him to my callback URL in my website I want to get OrderId from the session but it seems that is null. I want to know why it happens?
and another problem is that I use cookie authentication in identity but it doesn't work too. I have set it to 15 days .but it doesn't work too.I don't know but maybe these two problems are related to each other.if someone knows
Why my asp.net identity -user will log out automatically
public ActionResult Pay()
{
Session["orderid"]=12;
}
//callbackurl
public ActionResult Result()
{
var orderid=Convert.ToInt32( Session["orderid"]);//is null
}
the below is in my web.config
<system.web>
<authentication mode="Forms">
<forms loginUrl="~/User/Login" timeout="30">
</forms>
</authentication>
<sessionState timeout="30"></sessionState>
</system.web>
and the below is in startup.cs file
public class Startup
{
public string Issuer { get; set; }
public void Configuration(IAppBuilder app)
{
Issuer = "http://mywebsite.ir/";
ConfigureOAuthTokenGeneration(app);
ConfigureOAuthTokenConsumption(app);
app.UseCors(CorsOptions.AllowAll);
GlobalConfiguration.Configure(WebApiConfig.Register);
AreaRegistration.RegisterAllAreas();
//app.UseWebApi(GlobalConfiguration.Configuration);
RouteConfig.RegisterRoutes(RouteTable.Routes);
//app.UseMvc(RouteConfig.RegisterRoutes);
//ConfigureWebApi(GlobalConfiguration.Configuration);
}
private void ConfigureOAuthTokenGeneration(IAppBuilder app)
{
app.CreatePerOwinContext(() => new LeitnerContext());
app.CreatePerOwinContext<LeitnerUserManager>(LeitnerUserManager.Create);
app.CreatePerOwinContext<LeitnerRoleManager>(LeitnerRoleManager.Create);
// Plugin the OAuth bearer JSON Web Token tokens generation and Consumption will be here
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new Microsoft.Owin.PathString("/User/Login"),
ExpireTimeSpan = TimeSpan.FromDays(15),
Provider = new CookieAuthenticationProvider
{
OnApplyRedirect = ctx =>
{
if (!IsForApi(ctx.Request))
{
ctx.Response.Redirect(ctx.RedirectUri);
}
}
}
});
OAuthAuthorizationServerOptions options = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/api/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(15),
Provider = new LeitnerOAuthProvider(),
AccessTokenFormat = new LeitnerJwtFormat(Issuer),
};
app.UseOAuthAuthorizationServer(options);
//app.UseJwtBearerAuthentication(options);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
//app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
}
private bool IsForApi(IOwinRequest request)
{
IHeaderDictionary headers = request.Headers;
return ((headers != null) && ((headers["Accept"] == "application/json") || (request.Path.StartsWithSegments(new PathString("/api")))));
}
private void ConfigureOAuthTokenConsumption(IAppBuilder app)
{
var a = AudiencesStore.AudiencesList["LeitnerAudience"];
string audienceId = a.ClientId;// ConfigurationManager.AppSettings["as:AudienceId"];
byte[] audienceSecret = TextEncodings.Base64Url.Decode(a.Base64Secret/*ConfigurationManager.AppSettings["as:AudienceSecret"]*/);
// Api controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { audienceId },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(Issuer, audienceSecret)
}
});
}
}
Edit
My WebHost is Plesk Onyx,In the Hosting Setting I see a setting Preferred domain that have three item to select
1- www.jooyabash.ir
2- jooyabash.ir
3- None
Description :Select the URL (either with or without the www. prefix) to which site visitors will be redirected via a SEO-safe HTTP 301 redirect.
When I set it to 1 or 3 i see that session will lost.but when i set it to 2 session until 10 min it will not lost and the payment will compelet in this time
Does any one know why?
I am trying to integrate my web service to authenticate using Azure AD. The response from Azure AD varies each time. When i open my site in firefox normal browser, the IsAuthenticated as true.
IsAuthenticatedAsTrue
Opening in a private browser, the IsAuthenticated is false.
IsAuthenticatedAsFalse
The only difference i can see is, the IsAuthenticated true is from ClaimsIdentity and IsAuthenticated false is from GenericIdentity.
The following is my startup.auth code.
public partial class Startup
{
private static string clientId = ConfigurationManager.AppSettings["ClientId"];
private static string aadInstance = ConfigurationManager.AppSettings["AADInstance"];
private static string tenantId = ConfigurationManager.AppSettings["TenantId"];
private static string postLogoutRedirectUri = ConfigurationManager.AppSettings["PostLogoutRedirectUri"];
private static string authority = aadInstance + tenantId;
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
PostLogoutRedirectUri = postLogoutRedirectUri
});
}
}
The following is my code to send the authentication request to AzureAD
public void LoginUsingAzure()
{
HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
This issue was fixed. The reason for this issue is dependency problem. Found the answer in the below stackoverflow link.
ASP.NET_SessionId + OWIN Cookies do not send to browser
Installing Kentor.OwinCookieSaver -Version 1.1.0 nuget package, solved my issue.
I am trying to reproduce this issue however failed. I am testing the web app using version 50.1.0 with clean installation.
To fix this issue, I suggest that you re-install the Firefox with this version.
Here is the test figure for your reference: