I'm trying to run WIF Relying Party application on a shared host. They will not set the IIS Setting LoadUserProfile to true and as such I'm getting the following error:
Message: The data protection operation was unsuccessful. This may have been caused by not having the user profile loaded for the current thread's user context, which may be the case when the thread is impersonating. ExceptionStackTrace: at System.Security.Cryptography.ProtectedData.Protect(Byte[] userData, Byte[] optionalEntropy, DataProtectionScope scope) at Microsoft.IdentityModel.Web.ProtectedDataCookieTransform.Encode(Byte[] value)
Is there anyway around this?
Yes, this is because you are using the default token encryption which relies on DPAPI. You can replace that with certficate based encryption. See here: http://msdn.microsoft.com/en-us/library/ff803371.aspx (scroll to "There is one further change to the application..." )
The code is:
void OnServiceConfigurationCreated(object sender, ServiceConfigurationCreatedEventArgs e)
{
var sessionTransforms =
new List<CookieTransform>(
new CookieTransform[]
{
new DeflateCookieTransform(),
new RsaEncryptionCookieTransform(
e.ServiceConfiguration.ServiceCertificate),
new RsaSignatureCookieTransform(
e.ServiceConfiguration.ServiceCertificate)
});
var readOnlyTransforms = sessionTransforms.AsReadOnly();
var sessionHandler = new SessionSecurityTokenHandler(readOnlyTransforms);
e.ServiceConfiguration.SecurityTokenHandlers.AddOrReplace(sessionHandler);
}
and
void Application_Start(object sender, EventArgs e)
{
FederatedAuthentication.ServiceConfigurationCreated += OnServiceConfigurationCreated;
}
Both on the global.asax.cs
BTW, this is also the "web farm friendly" way of configuring WIF, so it is machine (instance) independant. Windows Azure deployments are essentially web farms, so that's why you see it in that chapter.
Update: In newer versions the API has changed. The updated code would look like this
void OnFederationConfigurationCreated(object sender, FederationConfigurationCreatedEventArgs e)
{
var sessionTransforms = new List<CookieTransform>(
new CookieTransform[]
{
new DeflateCookieTransform(),
new RsaEncryptionCookieTransform(e.FederationConfiguration.ServiceCertificate),
new RsaSignatureCookieTransform(e.FederationConfiguration.ServiceCertificate)
});
var sessionHandler = new SessionSecurityTokenHandler(sessionTransforms.AsReadOnly());
e.FederationConfiguration
.IdentityConfiguration
.SecurityTokenHandlers
.AddOrReplace(sessionHandler);
}
and
protected void Application_Start()
{
FederatedAuthentication.FederationConfigurationCreated += OnFederationConfigurationCreated;
}
You could also use MachineKeySessionSecurityTokenHandler which is available in .net 4.5 in the namespace System.IdentityModel.Services.Tokens. You can enable this token handler by setting it in the configuration.
<system.identityModel>
<identityConfiguration>
<securityTokenHandlers>
<remove type="System.IdentityModel.Tokens.SessionSecurityTokenHandler,
System.IdentityModel, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=B77A5C561934E089" />
<add type="System.IdentityModel.Services.Tokens.MachineKeySessionSecurityTokenHandler,
System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=B77A5C561934E089">
</add>
</securityTokenHandlers>
</identityConfiguration>
</system.identityModel>
Related
I have implemented my own JWT TokenHandler for the MobileAppService backend (ASP .Net, MVC).
The app service in ConfigureMobileApp is configured to use my custom TokenHandler instead of the AppServiceTokenHandler, like this:
public static void ConfigureMobileApp(IAppBuilder app)
{
....
HttpConfiguration httpConfig = new HttpConfiguration();
app.UseAppServiceAuthentication(new AppServiceAuthenticationOptions
{
SigningKey = ConfigurationManager.AppSettings["SigningKey"],
ValidAudiences = new[] { ConfigurationManager.AppSettings["ValidAudience"] },
ValidIssuers = new[] { ConfigurationManager.AppSettings["ValidIssuer"] },
TokenHandler = new TokenHandler() //httpConfig.GetAppServiceTokenHandler()
});
....
}
The custom TokenHandler is invoked, the JWT token is processed and the user is authorized. Everything is working perfectly fine. However, when I use the MobileServiceClient of my app to send messages, I see this exception being caught in the backend server log:
Microsoft.Azure.AppService.Authentication Warning: 0 : JWT validation failed: IDX10503: Signature validation failed. Keys tried: 'System.IdentityModel.Tokens.InMemorySymmetricSecurityKey
'.
Exceptions caught:
Since the exception is being caught, the application works fine. So what is happening is that the token is being sent simultaneously to both my custom TokenHandler and the default AppServiceTokenHandler. Since the JWT token has some claims which are not recognized by the default AppServiceTokenHandler, it throws (and catches) an exception.
Is there anyway I can force the AppServiceTokenHandler (or maybe other token handler which is there by default and I am not aware of) to be disabled?
Update:
Following Amor - MSFT's suggestion, I changed the code to what he proposed, setting the TokenHandler as
httpConfig.SetAppServiceTokenHandler(new TokenHandler());
but it didn't help. I also edited the web.config as follows, but it had no effect:
<system.identityModel>
<identityConfiguration>
<securityTokenHandlers>
<remove type="System.IdentityModel.Tokens.SessionSecurityTokenHandler, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<remove type="System.IdentityModel.Services.Tokens.MachineKeySessionSecurityTokenHandler, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<add type="MyApp.ASP.Handlers.TokenHandler, MyApp.ASP, Version=1.0.0.0, Culture=neutral" />
</securityTokenHandlers>
</identityConfiguration>
Also tried the following (as suggested here), still no effect:
<securityTokenHandlers>
<clear />
</securityTokenHandlers>
Is there anyway I can force the AppServiceTokenHandler (or maybe other token handler which is there by default and I am not aware of) to be disabled?
Please save your custom token handler to config.Properties before using it. We can invoke SetAppServiceTokenHandler method to do it.
var tokenHandler = new CustomTokenHandler();
httpConfig.SetAppServiceTokenHandler(tokenHandler);
Based on the document of AuthenticationHttpConfigurationExtensions.cs, SetAppServiceTokenHandler method will help us save the custom token handle to config.Properties and it will return the custom handle when the config.GetAppServiceTokenHandler method is invoked. Your final code could be like this,
HttpConfiguration httpConfig = new HttpConfiguration();
var tokenHandler = new CustomTokenHandler();
httpConfig.SetAppServiceTokenHandler(tokenHandler);
app.UseAppServiceAuthentication(new AppServiceAuthenticationOptions
{
// This middleware is intended to be used locally for debugging. By default, HostName will
// only have a value when running in an App Service application.
SigningKey = ConfigurationManager.AppSettings["SigningKey"],
ValidAudiences = new[] { ConfigurationManager.AppSettings["ValidAudience"] },
ValidIssuers = new[] { ConfigurationManager.AppSettings["ValidIssuer"] },
TokenHandler = httpConfig.GetAppServiceTokenHandler()
});
Edit 5/22/2017
Since you are using AppServiceAuthenticationMiddleware, please make sure you disabled the Authentication / Authorization in Azure portal.
tl;dr: What is the Owin equivalent of the HttpApplication.AuthenticateRequest event?
Background
When running an ASP.net site on IIS, the global System.Web.HttpApplication object raises an AuthenticateRequest event during each request.
Various http modules (such as the built-in FormsAuthentication) can attach to the event. The event handlers are called in the order in which they are registered. The first handler to set HttpContext.Current.User is the authentication used.
The job of the modules that are subscribed to this event is to set HttpContext.Current.User to to some Principal:
IIdentity identity = new GenericIdentity("MBurns", "ContosoAuthentcation");
IPrincipal principal = new GenericPrincipal(identity, null);
HttpContext.Current.User = principal;
Once HttpContext.Current.User is assigned, ASP.net knows that the user has been authenticated. (And once a user has been authenticated, they are no longer anonymous).
Any Module Can Do It
Anyone can use web.config to register their own IHttpModule with ASP.net:
web.config
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="MySuperCoolAuthenticationModule" type="ContosoAuthModule" />
</modules>
</system.webServer>
The module is easy enough to write. You implement the lone Init method of the IHttpModule interface. For us, we add ourself as an AuthenticateRequest event handler:
public class ContosoAuthModule : IHttpModule
{
public void Init(HttpApplication httpApplication)
{
// Register event handlers
httpApplication.AuthenticateRequest += OnApplicationAuthenticateRequest;
}
}
And then you can do what is needed to authenticate the user, and if they are a valid user, set the HttpContext.Current.User:
private void OnApplicationAuthenticateRequest(object sender, EventArgs e)
{
var request = HttpContext.Current.Request;
String username = SomeStuffToFigureOutWhoIsMakingTheRequest(request);
if (String.IsNullOrWhiteSpace(username))
{
//I don't know who they are :(
return;
}
//I know who they are, they are [username]!
IIdentity identity = new GenericIdentity(username, "ContosoSuperDuperAuthentication");
HttpContext.Current.User = new GenericPrincipal(identity, null);
}
That's all HttpApplication
MSDN documents the various events that are thrown by HttpApplication, and in what order:
ASP.NET Application Life Cycle Overview for IIS 7.0 (archive.is)
Validate the request, which examines the information sent by the browser and determines whether it contains potentially malicious markup. For more information, see ValidateRequesta and Script Exploits Overviewa.
Perform URL mapping, if any URLs have been configured in the UrlMappingsSectiona section of the Web.config file.
Raise the BeginRequest event.
Raise the AuthenticateRequesta event.
Raise the PostAuthenticateRequest event.
Raise the AuthorizeRequest event.
Raise the PostAuthorizeRequest event.
Raise the ResolveRequestCache event.
And that's all great when it's ASP.net and HttpApplication. Everything's well understood, easy enough to explain (in the half-screenful above), and works.
But HttpApplication is old and busted.
Owin is the new hotness
Everything is supposed to be Owin now. HttpApplication lives in System.Web. People want to be isolated from System.Web. They want this thing called Owin to be in charge now.
To further that goal, they (i.e. any new ASP.net MCV, web-forms, or SignalR web-site) disables the authentication system of ASP.net completely:
<system.web>
<authentication mode="None" />
</system.web>
So no more HttpApplication.AuthenticateRequest event. :(
What is the Owin equivalent?
What is the Owin equivalent of HttpApplication.AuthenticateRequest?
It's safe to say that no matter where my code is called from, my job is still to set HttpContext.Current.User to an identity.
Is it safe to say that no matter where my code is called form, my job is still to set HttpContext.Current.User to an identity?
What is the Owin equivalent of HttpApplication.AuthenticateRequest?
Attempt that doesn't work
Nothing of it is ever called:
using System;
using System.Threading.Tasks;
using Microsoft.Owin;
using Owin;
using System.Web;
using System.IO;
using Microsoft.Owin.Extensions;
using System.Security.Claims;
using System.Security.Principal;
[assembly: OwinStartup("AnyStringAsLongAsItsNotBlank", typeof(BasicAuthOwin))]
public class BasicAuthOwin
{
public void Configuration(IAppBuilder app)
{
app.Use((context, next) =>
{
System.Diagnostics.Trace.WriteLine("They did their best, shoddily-iddly-iddly-diddly");
OnAuthenticateRequest(context);
return next.Invoke();
});
app.UseStageMarker(PipelineStage.Authenticate);
app.Run(context =>
{
return context.Response.WriteAsync("Hello world");
});
}
private void OnAuthenticateRequest(IOwinContext context)
{
var request = context.Request;
String username = SomeStuffToFigureOutWhoIsMakingTheRequest(request);
if (String.IsNullOrWhiteSpace(username))
{
//I don't know who they are :(
return;
}
//I know who they are, they are [username]!
IIdentity identity = new GenericIdentity(username, "ContosoSuperDuperOwinAuthentication");
context.Authentication.User = new ClaimsPrincipal(identity);
}
private string SomeStuffToFigureOutWhoIsMakingTheRequest(IOwinRequest request)
{
//if ((System.Diagnostics.Stopwatch.GetTimestamp % 3) == 0)
// return "";
return "MBurns";
}
}
Check out the blog post from this website Jwt Authentication in ASP.NET WEB API AND MVC. It explains how to solve to issue of "Authorization has been denied for this request" using OWIN.
The JWTHandler class
public static void OnAuthenticateRequest(IOwinContext context)
{
var requestHeader = context.Request.Headers.Get("Authorization");
int userId = Convert.ToInt32(JwtDecoder.GetUserIdFromToken(requestHeader).ToString());
var identity = new GenericIdentity(userId.ToString(), "StakersClubOwinAuthentication");
//context.Authentication.User = new ClaimsPrincipal(identity);
var token = requestHeader.StartsWith("Bearer ") ? requestHeader.Substring(7) : requestHeader;
var secret = WebConfigurationManager.AppSettings.Get("jwtKey");
Thread.CurrentPrincipal = ValidateToken(
token,
secret,
true
);
context.Authentication.User = (ClaimsPrincipal) Thread.CurrentPrincipal;
//if (HttpContext.Current != null)
//{
// HttpContext.Current.User = Thread.CurrentPrincipal;
//}
}
The Startup class
public class Startup
{
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
app.Use((context, next) =>
{
JwtAuthHandler.OnAuthenticateRequest(context); //the new method
return next.Invoke();
});
app.UseStageMarker(PipelineStage.Authenticate);
WebApiConfig.Register(config);//Remove or comment the config.MessageHandlers.Add(new JwtAuthHandler()) section it would not be triggered on execution.
app.UseWebApi(config);
}
}
I am using VS2010 Report Viewer control in web application. The applications sessionstate mode is set to StateServer as follows
<sessionState timeout="30" mode="StateServer" stateConnectionString="tcpip=127.0.0.1:42424" sqlConnectionString="data source=127.0.0.1;Trusted_Connection=yes" cookieless="false" />
The reportviewer control is working fine on my devlopment machine but when the applicaiton is deployed onto server and when the reportviewer control page is loaded the following error is thrown.. All the other pages are working fine.
"Unable to serialize the session state. In 'StateServer' and 'SQLServer' mode, ASP.NET will serialize the session state objects, and as a result non-serializable objects or MarshalByRef objects are not permitted. The same restriction applies if similar serialization is done by the custom session state store in 'Custom' mode."
Can anyone please help, any idea will be of great help..
Thanks in advance.
rptvw.ProcessingMode = ProcessingMode.Remote;
rptvw.ServerReport.ReportServerUrl = new Uri("http://localhost:90/reportserver");
rptvw.ServerReport.ReportPath = string.Format("/Reports/{0}", reportName);
var param = new ReportParameter[4];
param[0] = new ReportParameter("Parameter1", DropDownListCodes.SelectedValue));
param[1] = new ReportParameter("Parameter2", DropDownListQuarters.SelectedValue));
param[2] = new ReportParameter("Parameter3", DropDownListComparators.SelectedValue));
param[3] = new ReportParameter("Parameter4", comptype);
rptvw.ServerReport.SetParameters(param);
rptvw.ServerReport.Refresh();
I managed to get it to work.
I followed this link for my solution msdn link
"When implementing the IReportServerCredentials interface, it is important know that the ReportViewer control stores the instance of the object in ASP.NET session. If the server's ASP.NET session is being stored out of process, such as in Reporting Services, the class must be marked Serializable so that it may be serialized for storage." taken from above link.
Created a new file in App_Code\ReportServerConnection.cs
[Serializable]
public sealed class ReportServerConnection : IReportServerConnection2
{
public bool GetFormsCredentials(out Cookie authCookie, out string userName, out string password, out string authority)
{
authCookie = null;
userName = null;
password = null;
authority = null;
// Not using form credentials
return false;
}
public WindowsIdentity ImpersonationUser
{
// Use the default Windows user. Credentials will be
// provided by the NetworkCredentials property.
get { return null; }
}
public ICredentials NetworkCredentials
{
get
{
// Read the user information from the web.config file. By reading the information on demand instead of
// storing it, the credentials will not be stored in session, reducing the vulnerable surface area to the
// web.config file, which can be secured with an ACL.
// User name
string userName = ConfigurationManager.AppSettings["ReportViewerUser"];
if (string.IsNullOrEmpty(userName))
throw new InvalidOperationException("Please specify the user name in the project's Web.config file.");
// Password
string password = ConfigurationManager.AppSettings["ReportViewerPassword"];
if (string.IsNullOrEmpty(password))
throw new InvalidOperationException("Please specify the password in the project's Web.config file");
// Domain
string domain = ConfigurationManager.AppSettings["ReportViewerDomain"];
if (string.IsNullOrEmpty(domain))
throw new InvalidOperationException("Please specify the domain in the project's Web.config file");
return new NetworkCredential(userName, password, domain);
}
}
public Uri ReportServerUrl
{
get
{
string url = ConfigurationManager.AppSettings["ReportServerUrl"];
if (string.IsNullOrEmpty(url))
throw new InvalidOperationException("Please specify the report server URL in the project's Web.config file");
return new Uri(url);
}
}
public int Timeout
{
// set timeout to 60 seconds
get { return 60000; }
}
public IEnumerable<Cookie> Cookies
{
// No custom cookies
get { return null; }
}
public IEnumerable<string> Headers
{
// No custom headers
get { return null; }
}
}
On the Report.aspx.cs page
protected void Page_Init(object sender, EventArgs e)
{
rptvw.ServerReport.ReportServerCredentials = new ReportServerConnection();
}
Changed this line in the code on the main post
rptvw.ServerReport.ReportServerUrl = rsc.ReportServerUrl;
And in the Web.config
<appSettings>
<add key="ReportViewerServerConnection" value=" App_Code.ReportServerConnection, App_Code"/>
<add key="ReportViewerUser" value="username"/>
<!-- Used as the user name by the ReportServerConnection class. -->
<add key="ReportViewerPassword" value="password"/>
<!-- Used as the password by the ReportServerConnection class. -->
<add key="ReportViewerDomain" value="domainname"/>
<!-- Used as the domain by the ReportServerConnection class. -->
<add key="ReportServerUrl" value="http://localhost:90/reportserver"/>
<!-- Used as the report server URL by the ReportServerConnection class. -->
I want user to select preferred language once for the whole app. suggest me the easiest possible steps.
Such that user select preferred language only once just after login and then all app's view rendered with selected culture.
I found somehting related here
Because i am new in Internationalization i am not getting it properly.
I created a sample application which is working fine with browser specific Culture but here i want user to select preferred language.
Typically, .NET will use the CultureSetting that best matches the user and then determines the appropriate resource file to use for globalization.
Once you have "saved" the users appropriate culture be in a database, session, or cookie.
What I do is change the thread to the appropriate language:
var language = "fr"; // Pull from your "saved" location (database, session, cookie, etc.)
// This changes UI only
Thread.CurrentThread.CurrentUICulture = new CultureInfo(language);
// This changes how number, date formatting is handled
Thread.CurrentThread.CurrentCulture = new CultureInfo(language);
Hope that helps!
I propose you to extend User entity with Culture parameter to store it in DB.
Then it possible to add information about user culture in Session for example (but any other technique also could be used)
then you need to add new Attribute with code
public class LocalizationAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
try
{
string culture = UserSession.Culture;//Other mechanism of getting userinfo could be used there
if (!string.IsNullOrEmpty(culture))
{
CultureInfo cultureInfo = new CultureInfo(culture);
Thread.CurrentThread.CurrentCulture = cultureInfo;
Thread.CurrentThread.CurrentUICulture = cultureInfo;
}
}
catch (Exception e)
{
Logger logger = new Logger();
logger.LogException(e);
}
}
}
And add this attribute to all your controllers which should be localized
One way to do it is with a cookie.
Assuming you have a drop-down list or some kind of input where the user can choose the language, have that input post to an action method. In that action method, write the language id to a cookie:
public class LanguageController
{
[ActionName("change-to")]
public virtual RedirectResult Change(LanguageChanger model)
{
var langCookie = new HttpCookie(CookieNames.Language);
langCookie.Value = model.SelectedIsoCode;
langCookie.Expires = DateTime.UtcNow.AddDays(28);
HttpContext.Response.Cookies.Add(langCookie);
return Redirect(model.ReturnUrl);
}
}
You can then use an HttpModule to set the culture for every request. This way you know the culture is set throughout the entire request processing pipeline, instead of just when your controller actions execute:
public class CookieLocalizationModule : IHttpModule
{
public void Dispose() { }
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(context_BeginRequest);
}
void context_BeginRequest(object sender, EventArgs e)
{
// read the cookie (if any) and set the culture
if (HttpContext.Current.Request.Cookies[CookieNames.Language] != null)
{
var cookie = HttpContext.Current.Request
.Cookies[CookieNames.Language];
var lang = cookie.Value;
var culture = new System.Globalization.CultureInfo(lang);
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = culture;
}
}
}
To register the module with IIS & IIS Express, see this web.config (note the Cassini config would be a little different)
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<modules runAllManagedModulesForAllRequests="true">
<add name="CookieLocalizationModule"
type="MyProject.CookieLocalizationModule, MyProject" />
</modules>
<handlers>
...
How to Override createUser() Membership method to display custom error message when password check fails??
I Used the Web Site Administration Tool, which provides a wizard-like interface for creating new users. (To start this tool, click ASP.NET Configuration on the Website menu in the Microsoft Visual Studio)
Web.Config file:
<membership defaultProvider="MyMembershipProvider">
<providers>
<clear />
<add name="MyMembershipProvider" type="BlueDDApp.Controllers.MyMembershipProvider" connectionStringName="ApplicationServices" enablePasswordRetrieval="false" minRequiredPasswordLength="8" minRequiredNonalphanumericCharacters="0" passwordStrengthRegularExpression="^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])|(?=.*[a-z])(?=.*[A-Z])(?=.*[!%,.;:])|(?=.*[a-z])(?=.*[0-9])(?=.*[!%,.;:])|(?=.*[A-Z])(?=.*[0-9])(?=.*[!%,.;:])$" passwordAttemptWindow="10" applicationName="/" />
</providers>
</membership>
Custom Membership class::
public class MyMembershipProvider : SqlMembershipProvider
{
public MyMembershipProvider()
{
//Membership.ValidatingPassword += new MembershipValidatePasswordEventHandler(OnValidatePassword);
ValidatingPassword += ValidatePassword;
}
/* public override MembershipUser CreateUser( string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
{
ValidatingPassword += ValidatePassword;
return base.CreateUser(username, password, email, passwordQuestion, passwordAnswer, isApproved, providerUserKey, out status);
}*/
void ValidatePassword(object sender, ValidatePasswordEventArgs e)
{
Regex check = new Regex("^(?i)(?!.*" + e.UserName + ").*$");
if (!check.IsMatch(e.Password))
{
e.FailureInformation = new HttpException("blah blah");
e.Cancel = true;
}
}
}
If you are using asp:CreateUserWizard control, which I presume you are ( it will connect to a membership provider from your web.config ), then :
In Design mode, if you click on this control, in the top right corner you have an icon, sort of an arrow, and there you can choose "Customize Create User Step" option. This will transform the control, expanding it into a separate controls that are used inside. Now you can access error message ( Literal control ) and change it to display static message, or display dynamically changing messages from code.
You can also add events to the CreateUserWizard like CreatingUser, CreateUserError and CreatedUser which will let you customize the behavior and how the creation is being used even more.
Here is a great sample about custom MembershipUser:
Sample Membership Provider Implementation