Dot Net 5 CSP with a nonce - discrepancy between nonce in header and nonce in script tags - content-security-policy

I have a .Net 5 site and would like to implement a CSP using a nonce for scripts. I register ICspNonceService in my Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
...various services added
services.AddContentSecurityPolicyNonce();
}
Then I add my headers with some middleware
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
var nonceService = app.ApplicationServices.GetService(typeof(ICspNonceService));
app.UseSecurityHeadersMiddleware(new SecurityHeadersBuilder((ICspNonceService)nonceService).AddDefaultSecurePolicy().RemoveHeader("X-Powered-By"));
...various middleware
}
And in that middleware:
public SecurityHeadersBuilder AddContentSecurityPolicy()
{
var policy = new List<string>();
var contentSecurityPolicies = Enum.GetValues<ContentSecurityPolicy>();
foreach (var contentSecurityPolicy in contentSecurityPolicies)
{
var value = contentSecurityPolicy.Value();
if (!string.IsNullOrEmpty(value))
{
value = value.Replace("{nonceValue}", this._cspNonceService.GetNonce());
policy.Add(value);
}
}
this._policy.SetHeaders[ContentSecurityPolicyConstants.Header] = string.Join(';', policy.ToArray());
return this;
}
And this produces my appropriate header:
default-src 'self'; script-src 'nonce-h5BUValX03OJFO165044o1gMNVUMsbhdYUvzpDATneU=' 'strict-dynamic'
Then I have a taghelper to add my nonce to a script tag:
[HtmlTargetElement("script", Attributes = "use-csp-nonce")]
public class NonceTagHelper : TagHelper
{
private readonly ICspNonceService _nonceService;
public NonceTagHelper(ICspNonceService nonceService) => this._nonceService = nonceService;
[HtmlAttributeName("use-csp-nonce")]
public bool UseNonce { get; set; }
public override Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
if (this.UseNonce)
{
output.Attributes.SetAttribute("nonce", this._nonceService.GetNonce());
}
return base.ProcessAsync(context, output);
}
}
Which I can use like this:
<script use-csp-nonce="true" src="/dist/js/main.min.js" asp-append-version="true" ></script>
This produces a script tag with a nonce, but it is not the same nonce as the header, so it all fails.
I have several theories:
Something about the order of my services registrations
The way I am injecting the ICspNonceService for the header
How do I ensure I get the same Nonce?

Related

extended OrmLiteAuthRepository not binding properly

I extended the class OrmLiteAuthRepository
In the app host i inject it into the container. I test it using requiredrole controller and it never calls the methods for my custom security checks.
Even though i get redirect to http://localhost:5000/?redirect=%2fRequiresRole#f=Unauthorized
SO i just verified the the main simple contrustor is called when teh application starts. So it is using my clss. but not calling the get\haspermission methods.
[RequiredRole("TheRole")]
public class RequiresRoleController : ServiceStackController
{
public ActionResult Index()
{
var session = SessionAs<CustomUserSession>();
return View(session);
}
}
using System;
using System.Collections.Generic;
using cbw.service.interfaces.Services;
using ServiceStack;
using ServiceStack.Auth;
using ServiceStack.Data;
using ServiceStack.Host;
using ServiceStack.Messaging;
namespace cbw.mvc.web.service.Providers
{
public class MyOrmLiteAuthRepository : OrmLiteAuthRepository
{
public MyOrmLiteAuthRepository(IDbConnectionFactory dbFactory) : base(dbFactory) { }
public MyOrmLiteAuthRepository(IDbConnectionFactory dbFactory, string namedConnnection = null)
: base(dbFactory, namedConnnection)
{
DbFactory = dbFactory;
NamedConnnection = namedConnnection;
}
public IDbConnectionFactory DbFactory { get; set; }
public string NamedConnnection { get; set; }
public override ICollection<string> GetPermissions(string userAuthId)
{
var permissions = base.GetPermissions(userAuthId);
using (var ss = HostContext.ResolveService<SecurityService>(new BasicRequest()))
{
permissions = ss.UserPermissions(Convert.ToInt32(userAuthId));
}
return permissions;
}
public override bool HasPermission(string userAuthId, string permission)
{
var hasPermission = base.HasPermission(userAuthId, permission);
using (var ss = HostContext.ResolveService<SecurityService>(new BasicRequest()))
{
hasPermission = ss.UserHasPermInRoleOrGroup(permission, Convert.ToInt32(userAuthId));
}
return hasPermission;
}
}
}
Apphost.cs
using System;
using System.Net;
using cbw.mvc.web.service.Providers;
using cbw.service.interfaces.Services;
using cbw.service.interfaces.Validators;
using cbw.service.models.Models;
using ServiceStack;
using ServiceStack.Auth;
using ServiceStack.OrmLite;
using ServiceStack.Caching;
using ServiceStack.Data;
using ServiceStack.Mvc;
using ServiceStack.Text;
using ServiceStack.Validation;
namespace cbw.mvc.web.service
{
public class AppHost : AppHostBase
{
public AppHost() : base("ServiceStack + .NET Core", typeof(StartupService).Assembly) { }
public override void Configure(Funq.Container container)
{
Plugins.Add(new RazorFormat());
//Works but recommend handling 404 at end of .NET Core pipeline
//this.CustomErrorHttpHandlers[HttpStatusCode.NotFound] = new RazorHandler("/notfound");
this.CustomErrorHttpHandlers[HttpStatusCode.Unauthorized] = new RazorHandler("/login");
//To include null values in the json globally
JsConfig.IncludeNullValues = true;
//This is mandate. We need "IncludeNullValuesInDictionaries = true" to include null values
JsConfig.IncludeNullValuesInDictionaries = true;
//To automatically wired up for you on all HTTP Verbs (GET, POST, etc)
//And built-in endpoints, i.e. JSON, XML, JSV, HTML, CSV, SOAP
Plugins.Add(new CorsFeature());
//To add registration feature
Plugins.Add(new RegistrationFeature());
//To add validation feature
Plugins.Add(new ValidationFeature());
container.RegisterValidators(
typeof(InsertCompanyValidator).Assembly,
typeof(UpdateCompanyValidator).Assembly,
typeof(DeleteCompanyValidator).Assembly
);
Plugins.Add(new AuthFeature(() => new CustomUserSession(),
new IAuthProvider[]
{
new CredentialsAuthProvider(), //HTML Form post of UserName/Password credentials
new BasicAuthProvider(), //Sign-in with HTTP Basic Auth
new DigestAuthProvider(AppSettings), //Sign-in with HTTP Digest Auth
new TwitterAuthProvider(AppSettings), //Sign-in with Twitter
new FacebookAuthProvider(AppSettings), //Sign-in with Facebook
new GithubAuthProvider(AppSettings), //Sign-in with GitHub OAuth Provider
new GoogleAuthProvider(AppSettings), //Sign-in with Google OAuth Provider
new YandexAuthProvider(AppSettings), //Sign-in with Yandex OAuth Provider
new VkAuthProvider(AppSettings), //Sign-in with VK.com OAuth Provider
})
{
HtmlRedirect = "/",
//IncludeRegistrationService = true,
});
// i.e. Register in Memory Cache Client
//AutoQuery
Plugins.Add(new AutoQueryFeature { MaxLimit = 100000 });
container.Register<IAuthRepository>(c =>
new MyOrmLiteAuthRepository(c.Resolve<IDbConnectionFactory>())
{
UseDistinctRoleTables = AppSettings.Get("UseDistinctRoleTables", true),
});
;
bool ShouldWipeAndReloadDb = false;
var environmentVariable = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
if (environmentVariable == "LocalMemory" || environmentVariable == "LocalSQLServer")
{
ShouldWipeAndReloadDb = true;
//Init auth tables
container.Resolve<IAuthRepository>().InitSchema();
}
var authRepo = (MyOrmLiteAuthRepository)container.Resolve<IAuthRepository>();
//Wipe and reload if using in memory SQL
if (ShouldWipeAndReloadDb)
{
DatabaseInitService dis = new DatabaseInitService();
dis.ResetDatabase();
SessionService.ResetUsers(authRepo);
dis.InitializeTablesAndData();
}
}
}
}
You’re using [RequiredRole] but you’re only overriding the HasPermission/GetPermissions APIs, you need to have overridden the HasRole/GetRoles APIs instead.

ASP.NET Core MVC 6 Azure B2C Active Directory Authentication issue

I followed the article : https://github.com/Azure-Samples/active-directory-dotnet-webapp-openidconnect-aspnetcore-b2c
In this sample app there is a Sign-in button. I am able to Sign-in successfully by clicking Sign-In button by providing my Azure B2C Tenant and registering the application in the tenant.
In another app, I want to authenticate without the Sign-In button being clicked i.e. right when I open the URL, I get redirected first to the Azure B2C AD login page, and after successful validation of credentials, I should be able to see the home screen.
So, what I did was from the URL mentioned from the article, I copied the SiginIn() method as:
public async Task<IActionResult> Index()
{
await SignIn();
await GetDataAsync();
}
I get an error message on running the application as : InvalidOperationException: No authentication handler is configured to handle the scheme: b2c_1_org_b2c_global_signin
Please advise how can I authenticate directly without the signin button. Previously with MVC5, I have successfully done this where I used [Authorize] attribute on the Controller class.
Controller Code with Index method
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using System.IO;
using System.Net;
using System.Text;
using Newtonsoft.Json;
using Microsoft.AspNetCore.Hosting;
using WebViewerCore.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.AspNetCore.Authorization;
namespace WebViewerCore.Controllers
{
[Authorize]
public class DocumentController : Controller
{
#region GlobalVariables
private static readonly string serviceUrl = "";
private string doctype = string.Empty;
private string dmsno = string.Empty;
public string documentName = string.Empty;
private string errMsg = string.Empty;
StringBuilder msg;
Document doc;
private IHostingEnvironment _env;
private IConfiguration _config;
#endregion
#region C'tor
public DocumentController(IHostingEnvironment env, IConfiguration config)
{
_env = env;
_config = config;
}
#endregion
#region ControllerAction
public async Task<IActionResult> Index()
{
//return View();
try
{
//await SignIn();
string storageAccount = _config.GetSection("BlobStorage").GetSection("StorageAccount").Value;
string storageContainer = _config.GetSection("BlobStorage").GetSection("StorageContainer").Value;
ViewBag.StorageAccount = storageAccount;
ViewBag.StorageContainer = storageContainer;
await GetDataAsync();
//HttpContext.Response.ContentType = "application/vnd.ms-xpsdocument";
if (TempData["QueryStringMissing"] != null && (bool)TempData["QueryStringMissing"] || doc == null)
{
return View("View");
}
else
{
return View("Index", doc);
}
}
catch (Exception ex)
{
//logger.LogErrorWithMessage(ex, ex.StackTrace);
//return View("Error", new HandleErrorInfo(ex, "Document", "Index"));
throw ex;
}
}
#endregion
Startup.cs code
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.FileProviders;
using Microsoft.AspNetCore.Http;
using System.IO;
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authentication.Cookies;
namespace WebViewerCore
{
public class Startup
{
#region Global Variables
public static string SignUpPolicyId;
public static string SignInPolicyId;
public static string ProfilePolicyId;
public static string ClientId;
public static string RedirectUri;
public static string AadInstance;
public static string Tenant;
#endregion
public Startup(IHostingEnvironment env)
{
//var builder = new ConfigurationBuilder()
// .SetBasePath(env.ContentRootPath)
// .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
// .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
// .AddEnvironmentVariables();
//Configuration = builder.Build();
Configuration = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables()
.Build();
}
public IConfigurationRoot Configuration { get; set; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
// Adds a default in-memory implementation of IDistributedCache.
services.AddDistributedMemoryCache();
services.AddSession(options =>
{
// Set a short timeout for easy testing.
options.IdleTimeout = TimeSpan.FromSeconds(10);
options.CookieHttpOnly = true;
});
services.AddSingleton<IConfiguration>(Configuration);
// Add Authentication services.
services.AddAuthentication(sharedOptions => sharedOptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseSession();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Document}/{action=Index}/{id?}");
});
// App config settings
ClientId = Configuration["AzureAD:ClientId"];
AadInstance = Configuration["AzureAD:AadInstance"];
Tenant = Configuration["AzureAD:Tenant"];
RedirectUri = Configuration["AzureAD:RedirectUri"];
// B2C policy identifiers
SignUpPolicyId = Configuration["AzureAD:SignUpPolicyId"];
SignInPolicyId = Configuration["AzureAD:SignInPolicyId"];
// Configure the OWIN pipeline to use OpenID Connect auth.
//app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(SignUpPolicyId));
app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(SignInPolicyId));
}
private OpenIdConnectOptions CreateOptionsFromPolicy(string policy)
{
policy = policy.ToLower();
return new OpenIdConnectOptions
{
// For each policy, give OWIN the policy-specific metadata address, and
// set the authentication type to the id of the policy
MetadataAddress = string.Format(AadInstance, Tenant, policy),
AuthenticationScheme = policy,
CallbackPath = new PathString(string.Format("/{0}", policy)),
// These are standard OpenID Connect parameters, with values pulled from config.json
ClientId = ClientId,
PostLogoutRedirectUri = RedirectUri,
Events = new OpenIdConnectEvents
{
OnRemoteFailure = RemoteFailure,
},
ResponseType = OpenIdConnectResponseType.IdToken,
// This piece is optional - it is used for displaying the user's name in the navigation bar.
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
}
};
}
// Used for avoiding yellow-screen-of-death
private Task RemoteFailure(FailureContext context)
{
context.HandleResponse();
if (context.Failure is OpenIdConnectProtocolException && context.Failure.Message.Contains("access_denied"))
{
context.Response.Redirect("/");
}
else
{
context.Response.Redirect("/Home/Error?message=" + context.Failure.Message);
}
return Task.FromResult(0);
}
}
}
To automatically redirect users navigating to a specific controller or endpoint in MVC, all you need to do is add the [Authorize] attribute, provided you've configured your middleware correctly.
In the case of Azure AD B2C, you need to make sure you add the OpenID Middleware like so:
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
// For each policy, give OWIN the policy-specific metadata address, and
// set the authentication type to the id of the policy
MetadataAddress = string.Format(AadInstance, Tenant, policy),
AuthenticationScheme = policy,
CallbackPath = new PathString(string.Format("/{0}", policy)),
// These are standard OpenID Connect parameters, with values pulled from config.json
ClientId = ClientId,
PostLogoutRedirectUri = RedirectUri,
Events = new OpenIdConnectEvents
{
OnRemoteFailure = RemoteFailure,
},
ResponseType = OpenIdConnectResponseType.IdToken,
// This piece is optional - it is used for displaying the user's name in the navigation bar.
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
},
};);
The sample you referenced already does what you want, to a point.
In that sample, if you haven't signed in and navigate to /about, you'll get automatically redirected to Azure AD B2C to sign in.
As it stands, the sample has the [Authorize] attribute only on a controller action in the Home controller, you'll want to move that up to the controller level and add it to every controller. Then you can just remove the the sign-in/sign-up/sign-out buttons and controller action.

UserId not found error in aspnet Identity at GenerateUserIdentityAsync method

I am getting UserId not found error after registring a user and also after login.Moreoever, after registration, data is saved to database and in dbo.AspNetUsers table, id column is auto incremented and return type is int.
There is UserId Column in AspNetUserClaims table.It has 4 Col---Id,UserId,ClaimType,ClaimValue.It has Id column as auto incremented not the userId.
I was initially successfully changed Primary key from string to int by following this link---http://www.asp.net/identity/overview/extensibility/change-primary-key-for-users-in-aspnet-identity.
It was running succesfully before but now it is giving me error at this line---
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser, int> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
Exception Details: System.InvalidOperationException: UserId not found.
This is the complete stack trace. you can see it here----http://pastebin.com/0hp5eAnp
It was working fine earlier but now when i added foreign key relationship with other tables, i don't know what is missing there. In the database all the tables are created properly with proper relationship between them but something is missing here.
My ApplicationUser class is something like this-------
public class ApplicationUser : IdentityUser<int, CustomUserLogin, CustomUserRole, CustomUserClaim>
{
public ApplicationUser()
{
this.Posts = new HashSet<Post>();
}
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public virtual ICollection<Post> Posts { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser, int> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
public class CustomUserRole : IdentityUserRole<int> { }
public class CustomUserClaim : IdentityUserClaim<int> { }
public class CustomUserLogin : IdentityUserLogin<int> { }
public class CustomRole : IdentityRole<int, CustomUserRole>
{
public CustomRole() { }
public CustomRole(string name) { Name = name; }
}
public class CustomUserStore : UserStore<ApplicationUser, CustomRole, int,
CustomUserLogin, CustomUserRole, CustomUserClaim>
{
public CustomUserStore(ApplicationDbContext context)
: base(context)
{
}
}
public class CustomRoleStore : RoleStore<CustomRole, int, CustomUserRole>
{
public CustomRoleStore(ApplicationDbContext context)
: base(context)
{
}
}
and my IdentityConfig.cs class file is something like this-------
// Configure the application user manager used in this application. UserManager is defined in ASP.NET Identity and is used by the application.
public class ApplicationUserManager : UserManager<ApplicationUser, int>
{
public ApplicationUserManager(IUserStore<ApplicationUser, int> store)
: base(store)
{
}
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new CustomUserStore(context.Get<ApplicationDbContext>()));
// Configure validation logic for usernames
manager.UserValidator = new UserValidator<ApplicationUser, int>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 1,
//RequireNonLetterOrDigit = true,
//RequireDigit = true,
//RequireLowercase = true,
//RequireUppercase = true,
};
// Configure user lockout defaults
manager.UserLockoutEnabledByDefault = true;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
manager.MaxFailedAccessAttemptsBeforeLockout = 5;
// Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
// You can write your own provider and plug it in here.
manager.RegisterTwoFactorProvider("Phone Code", new PhoneNumberTokenProvider<ApplicationUser, int>
{
MessageFormat = "Your security code is {0}"
});
manager.RegisterTwoFactorProvider("Email Code", new EmailTokenProvider<ApplicationUser, int>
{
Subject = "Security Code",
BodyFormat = "Your security code is {0}"
});
manager.EmailService = new EmailService();
manager.SmsService = new SmsService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser, int>(dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
}
// Configure the application sign-in manager which is used in this application.
public class ApplicationSignInManager : SignInManager<ApplicationUser, int>
{
public ApplicationSignInManager(ApplicationUserManager userManager, IAuthenticationManager authenticationManager)
: base(userManager, authenticationManager)
{
}
public override Task<ClaimsIdentity> CreateUserIdentityAsync(ApplicationUser user)
{
return user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager);
}
public static ApplicationSignInManager Create(IdentityFactoryOptions<ApplicationSignInManager> options, IOwinContext context)
{
return new ApplicationSignInManager(context.GetUserManager<ApplicationUserManager>(), context.Authentication);
}
}
i have seen many stackoverflow answers but not getting it to work.Can someone plzz plzz see what is missing, what should i do now.thanks in advance.
Here, in the applicationUser class, at the Id column, it showing some warning and message in tooltip like this-------
models.ApplicationUSer.ID hides inherited member
Microsoft.Aspnet.Identity.EntityFramework.IDentity
USer.Id. To make current member override
that implementation, add override keyword otherwise
add new keyword where x is just the namespace.
My StartUp.Auth.cs in App_Start folder is like this------
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
// 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, int>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager), getUserIdCallback:(id)=>(id.GetUserId<int>()))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
// Enables the application to remember the second login verification factor such as phone or email.
// Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
// This is similar to the RememberMe option when you log in.
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
// Uncomment the following lines to enable logging in with third party login providers
//app.UseMicrosoftAccountAuthentication(
// clientId: "",
// clientSecret: "");......................................................................
and my startUp.cs file is like this----
[assembly: OwinStartupAttribute(typeof(WebApp.Startup))]
namespace WebApp
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
}
}
You will have to pull out your ApplicationUserManager to have it nice and clean and implement more methods... For example see following post (It implemented all methods with your custom Key (TKey in the example):
http://www.symbolsource.org/MyGet/Metadata/aspnetwebstacknightly/Project/Microsoft.AspNet.Identity.Core/2.0.0-beta1-140211/Release/Default/Microsoft.AspNet.Identity.Core/Microsoft.AspNet.Identity.Core/UserManager.cs?ImageName=Microsoft.AspNet.Identity.Core
You will see that the error you receive GetSecurityStampAsync also is implemented there.

(Not Found) Error in Azure Mobile Services .NET Backend

Been stuck with that error till madness phases ... Please help
I have created an Azure Mobile Service .NET backend, and am now trying to call its Post function from a Xamarin Android client
I initialize and call the Insert async function (these are just snippets from my code)
private static IMobileServiceTable<Todo> _todoMobileServiceTable;
public static bool? InitializeAms()
{
try
{
CurrentPlatform.Init();
_mobileServiceClient = new MobileServiceClient(applicationUrl, applicationKey);
_todoMobileServiceTable = _mobileServiceClient.GetTable<Todo>();
return true;
}
catch (MalformedURLException malformedUrlException)
{
ReportHelper.Report(Tag, "There was an error creating the Mobile Service. Verify the URL", true, malformedUrlException);
}
catch (Exception exception)
{
ReportHelper.Report(Tag, "Error occurred during initialization of Azure Mobile Services", true, exception);
}
return null;
}
_todoMobileServiceTable.InsertAsync(Todo);
I get the following error when calling .InsertAsync(Todo)
The request could not be completed. (Not Found)
N.B:
Azure storage client is not available for xamarin yet, and I have no other choice other than to use this dirty fork which is 1 year old and is made for iOS not Android (although it works fine with azure mobile service javascript) https://github.com/zgramana/IOSAzureBlobUploader
It works if I use the browser 'try it out' button but it doesn't work when I call it from the xamarin client app.
It works from the xamarin client app if I use the javascript mobile service
This error occurs both on the local azure mobile service and the published one online
Here is the WebApiConfig class
namespace Service.Ams
{
public static class WebApiConfig
{
public static void Register()
{
// Use this class to set configuration options for your mobile service
ConfigOptions options = new ConfigOptions();
// Use this class to set WebAPI configuration options
HttpConfiguration config = ServiceConfig.Initialize(new ConfigBuilder(options));
// To display errors in the browser during development, uncomment the following
// line. Comment it out again when you deploy your service for production use.
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
Database.SetInitializer(new ServiceAmsInitializer());
}
}
public class ServiceAmsInitializer : ClearDatabaseSchemaIfModelChanges<ServiceAmsDbContext>
{}
}
Here is the TableController class
namespace Service.Ams.Controllers
{
public class TodoItemController : TableController<TodoItem>
{
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
ServiceAmsDbContext serviceAmsDbContext = new ServiceAmsDbContext();
DomainManager = new EntityDomainManager<TodoItem>(serviceAmsDbContext, Request, Services);
}
// GET tables/TodoItem
[AuthorizeLevel(AuthorizationLevel.Admin)]
public IQueryable<TodoItem> GetAllTodoItems()
{
return Query();
}
// GET tables/TodoItem/55D11C86-6EA6-4C44-AA33-337FC9A27525
[AuthorizeLevel(AuthorizationLevel.Admin)]
public SingleResult<TodoItem> GetTodoItem(string id)
{
return Lookup(id);
}
// PATCH tables/TodoItem/55D11C86-6EA6-4C44-AA33-337FC9A27525
[AuthorizeLevel(AuthorizationLevel.Admin)]
public Task<TodoItem> PatchTodoItem(string id, Delta<TodoItem> patch)
{
return UpdateAsync(id, patch);
}
// POST tables/TodoItem/55D11C86-6EA6-4C44-AA33-337FC9A27525
[AuthorizeLevel(AuthorizationLevel.Anonymous)]
public async Task<IHttpActionResult> PostTodoItem(TodoItem item)
{
string storageAccountName;
string storageAccountKey;
// Try to get the Azure storage account token from app settings.
if (
!(Services.Settings.TryGetValue("STORAGE_ACCOUNT_NAME", out storageAccountName) |
Services.Settings.TryGetValue("STORAGE_ACCOUNT_ACCESS_KEY", out storageAccountKey)))
Services.Log.Error("Could not retrieve storage account settings.");
// Set the URI for the Blob Storage service.
Uri blobEndpoint = new Uri(string.Format("http://127.0.0.1:10000/{0}/", storageAccountName));
// Create the BLOB service client.
CloudBlobClient blobClient = new CloudBlobClient(blobEndpoint, new StorageCredentials(storageAccountName, storageAccountKey));
// Create a container, if it doesn't already exist.
CloudBlobContainer container = blobClient.GetContainerReference(item.ContainerName);
await container.CreateIfNotExistsAsync();
// Create a shared access permission policy.
BlobContainerPermissions containerPermissions = new BlobContainerPermissions
{
PublicAccess = BlobContainerPublicAccessType.Blob
};
// Enable anonymous read access to BLOBs.
container.SetPermissions(containerPermissions);
// Define a policy that gives write access to the container for 5 minutes.
SharedAccessBlobPolicy sasPolicy = new SharedAccessBlobPolicy
{
SharedAccessStartTime = DateTime.UtcNow,
SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(5),
Permissions = SharedAccessBlobPermissions.Write
};
// Get the SAS as a string.
item.SasQueryString = container.GetSharedAccessSignature(sasPolicy);
// Set the URL used to store the image.
item.ImageLqUri = string.Format("{0}{1}/{2}", blobEndpoint, item.ContainerName, item.ResourceNameLq);
item.ImageHqUri = string.Format("{0}{1}/{2}", blobEndpoint, item.ContainerName, item.ResourceNameHq);
// Complete the insert operation.
TodoItem current = await InsertAsync(item);
return CreatedAtRoute("Tables", new {id = current.Id}, current);
}
// DELETE tables/TodoItem/55D11C86-6EA6-4C44-AA33-337FC9A27525
[AuthorizeLevel(AuthorizationLevel.Admin)]
public Task DeleteTodoItem(string id)
{
return DeleteAsync(id);
}
}
}
Here is the EntityData class
namespace Service.Ams.DataObjects
{
[Table("dbo.TodoItems")]
public class TodoItem : EntityData
{
public string ContainerName { get; set; }
public string ResourceNameLq { get; set; }
public string ResourceNameHq { get; set; }
public string SasQueryString { get; set; }
public string ImageLqUri { get; set; }
public string ImageHqUri { get; set; }
}
}
Is there any way you can get a dump of what the HTTP request looks like?
I don't have an android client handy here but we can have a look on Monday.
Henrik
TableController and client corresponding class must have the same name for example TodoController and TodoClass. I don't know if there is an attribute that modifies this rule and how to use, if at server side decorating TableController class or at client side decorating data class.

How to upload bundled and minified files to Windows Azure CDN

I'm using the ASP.NET MVC 4 bundling and minifying features in the Microsoft.AspNet.Web.Optimization namespace (e.g. #Styles.Render("~/content/static/css")).
I'd like to use it in combination with a Windows Azure CDN.
I looked into writing a custom BundleTransform but the content is not optimized there yet.
I also looked into parsing and uploading the optimized stream on runtime but that feels like a hack to me and I don't really like it:
#StylesCdn.Render(Url.AbsoluteContent(
Styles.Url("~/content/static/css").ToString()
));
public static IHtmlString Render(string absolutePath)
{
// get the version hash
string versionHash = HttpUtility.ParseQueryString(
new Uri(absolutePath).Query
).Get("v");
// only parse and upload to CDN if version hash is different
if (versionHash != _versionHash)
{
_versionHash = versionHash;
WebClient client = new WebClient();
Stream stream = client.OpenRead(absolutePath);
UploadStreamToAzureCdn(stream);
}
var styleSheetLink = String.Format(
"<link href=\"{0}://{1}/{2}/{3}?v={4}\" rel=\"stylesheet\" type=\"text/css\" />",
cdnEndpointProtocol, cdnEndpointUrl, cdnContainer, cdnCssFileName, versionHash
);
return new HtmlString(styleSheetLink);
}
How can I upload the bundled and minified versions automatically to my Windows Azure CDN?
Following Hao's advice I Extended Bundle and IBundleTransform.
Adding AzureScriptBundle or AzureStyleBundle to bundles;
bundles.Add(new AzureScriptBundle("~/bundles/modernizr.js", "cdn").Include("~/Scripts/vendor/modernizr.custom.68789.js"));
Results in;
<script src="//127.0.0.1:10000/devstoreaccount1/cdn/modernizr.js?v=g-XPguHFgwIb6tGNcnvnI_VY_ljCYf2BDp_NS5X7sAo1"></script>
If CdnHost isn't set it will use the Uri of the blob instead of the CDN.
Class
using System;
using System.Text;
using System.Web;
using System.Web.Optimization;
using System.Security.Cryptography;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsAzure.StorageClient;
namespace SiegeEngineWebRole.BundleExtentions
{
public class AzureScriptBundle : Bundle
{
public AzureScriptBundle(string virtualPath, string containerName, string cdnHost = "")
: base(virtualPath, null, new IBundleTransform[] { new JsMinify(), new AzureBlobUpload { ContainerName = containerName, CdnHost = cdnHost } })
{
ConcatenationToken = ";";
}
}
public class AzureStyleBundle : Bundle
{
public AzureStyleBundle(string virtualPath, string containerName, string cdnHost = "")
: base(virtualPath, null, new IBundleTransform[] { new CssMinify(), new AzureBlobUpload { ContainerName = containerName, CdnHost = cdnHost } })
{
}
}
public class AzureBlobUpload : IBundleTransform
{
public string ContainerName { get; set; }
public string CdnHost { get; set; }
static AzureBlobUpload()
{
}
public virtual void Process(BundleContext context, BundleResponse response)
{
var file = VirtualPathUtility.GetFileName(context.BundleVirtualPath);
if (!context.BundleCollection.UseCdn)
{
return;
}
if (string.IsNullOrWhiteSpace(ContainerName))
{
throw new Exception("ContainerName Not Set");
}
var conn = CloudStorageAccount.Parse(RoleEnvironment.GetConfigurationSettingValue("DataConnectionString"));
var blob = conn.CreateCloudBlobClient()
.GetContainerReference(ContainerName)
.GetBlobReference(file);
blob.Properties.ContentType = response.ContentType;
blob.UploadText(response.Content);
var uri = string.IsNullOrWhiteSpace(CdnHost) ? blob.Uri.AbsoluteUri.Replace("http:", "").Replace("https:", "") : string.Format("//{0}/{1}/{2}", CdnHost, ContainerName, file);
using (var hashAlgorithm = CreateHashAlgorithm())
{
var hash = HttpServerUtility.UrlTokenEncode(hashAlgorithm.ComputeHash(Encoding.Unicode.GetBytes(response.Content)));
context.BundleCollection.GetBundleFor(context.BundleVirtualPath).CdnPath = string.Format("{0}?v={1}", uri, hash);
}
}
private static SHA256 CreateHashAlgorithm()
{
if (CryptoConfig.AllowOnlyFipsAlgorithms)
{
return new SHA256CryptoServiceProvider();
}
return new SHA256Managed();
}
}
}
So there isn't a great way to do this currently. The longer term workflow we are envisioning is adding build-time bundling support. Then you would run a build task (or run an exe if you prefer) to generate the bundles and then be able to upload these to the AzureCDN. Finally, you just turn on UseCDN on the BundleCollection, and the Script/Style helpers would just automatically switch to rendering out links to your AzureCDN with proper fallback to your local bundles.
For the short term, what I think you are trying to do is upload your bundle to the AzureCDN when the bundle is first constructed?
A BundleTransform is one way to do it I guess, its a bit of a hack, but you could add a BundleTransform last in your bundle. Since its last, the BundleResponse.Content is effectively the final bundle response. At that point in time you can upload it to your CDN. Does that make sense?
You can define origin domain as Azure's website (this probably was added long after the original question).
Once you have CDN endpoint, you will need to allow query string for it and then you can reference directly to bundles via CDN:
<link href="//az888888.vo.msecnd.net/Content/css-common?v=ioYVnAg-Q3qYl3Pmki-qdKwT20ESkdREhi4DsEehwCY1" rel="stylesheet"/>
I've also created this helper to append CDN host name:
public static IHtmlString RenderScript(string virtualPath)
{
if (HttpContext.Current.IsDebuggingEnabled)
return Scripts.Render(virtualPath);
else
return new HtmlString(String.Format(
CultureInfo.InvariantCulture,
Scripts.DefaultTagFormat,
"//CDN_HOST" + Scripts.Url(virtualPath).ToHtmlString()));
}
For #manishKungwani requested in previous comment. Just set UseCdn and then use the
cdnHost overload to crate the bundle. I used this to put in an AWS CloudFront domain (xxx.cloudfront.net) but in hindsight it should have been named more generically to use with any other CDN provider.
public class CloudFrontScriptBundle : Bundle
{
public CloudFrontScriptBundle(string virtualPath, string cdnHost = "")
: base(virtualPath, null, new IBundleTransform[] { new JsMinify(), new CloudFrontBundleTransformer { CdnHost = cdnHost } })
{
ConcatenationToken = ";";
}
}
public class CloudFrontStyleBundle : Bundle
{
public CloudFrontStyleBundle(string virtualPath, string cdnHost = "")
: base(virtualPath, null, new IBundleTransform[] { new CssMinify(), new CloudFrontBundleTransformer { CdnHost = cdnHost } })
{
}
}
public class CloudFrontBundleTransformer : IBundleTransform
{
public string CdnHost { get; set; }
static CloudFrontBundleTransformer()
{
}
public virtual void Process(BundleContext context, BundleResponse response)
{
if (context.BundleCollection.UseCdn && !String.IsNullOrWhiteSpace(CdnHost))
{
var virtualFileName = VirtualPathUtility.GetFileName(context.BundleVirtualPath);
var virtualDirectory = VirtualPathUtility.GetDirectory(context.BundleVirtualPath);
if (!String.IsNullOrEmpty(virtualDirectory))
virtualDirectory = virtualDirectory.Trim('~');
var uri = string.Format("//{0}{1}{2}", CdnHost, virtualDirectory, virtualFileName);
using (var hashAlgorithm = CreateHashAlgorithm())
{
var hash = HttpServerUtility.UrlTokenEncode(hashAlgorithm.ComputeHash(Encoding.Unicode.GetBytes(response.Content)));
context.BundleCollection.GetBundleFor(context.BundleVirtualPath).CdnPath = string.Format("{0}?v={1}", uri, hash);
}
}
}
private static SHA256 CreateHashAlgorithm()
{
if (CryptoConfig.AllowOnlyFipsAlgorithms)
{
return new SHA256CryptoServiceProvider();
}
return new SHA256Managed();
}
}

Resources