Login via Xamarin Forms Application - asp.net-web-api

we have an mvc 5 application with individual user authentication we also have a xamarin forms application. we need to be able to use the same login details thats created on the web application when we log in via the xamarin application we are creating. we have successfully been able to create web api controllers using existing models in the web application and read/write the data in the xamarin application. but the problem is that we are not able to provide the same authentication we have(username and password with role assigned to the user) to the xamarin application. how can we make an api controller that reads from our existing database..please note our application is hosted on azure with a sql database.
basically we want to provide a login to our web application via the mobile app.

You need to take a look at Adrian Halls book - chapter 2 covers custom authentication which is what you need.
https://adrianhall.github.io/develop-mobile-apps-with-csharp-and-azure/chapter2/custom/
The key points are setting the mobile app to use authentication in the Azure portal but don't set any of the authentication providers (this makes it custom)
You then need to implement your own custom authentication controller to handle the authentication call back like this example taken from Adrian's book;
using System;
using System.IdentityModel.Tokens;
using System.Linq;
using System.Security.Claims;
using System.Web.Http;
using Microsoft.Azure.Mobile.Server.Login;
using Newtonsoft.Json;
namespace AWPBackend.Controllers
{
[Route(".auth/login/custom")]
public class CustomAuthController : ApiController
{
private MobileServiceContext db;
private string signingKey, audience, issuer;
public CustomAuthController()
{
db = new MobileServiceContext();
signingKey = Environment.GetEnvironmentVariable("WEBSITE_AUTH_SIGNING_KEY");
var website = Environment.GetEnvironmentVariable("WEBSITE_HOSTNAME");
audience = $"https://{website}/";
issuer = $"https://{website}/";
}
[HttpPost]
public IHttpActionResult Post([FromBody] User body)
{
if (body == null || body.Username == null || body.Password == null ||
body.Username.Length == 0 || body.Password.Length == 0)
{
return BadRequest(); ;
}
if (!IsValidUser(body))
{
return Unauthorized();
}
var claims = new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, body.Username)
};
JwtSecurityToken token = AppServiceLoginHandler.CreateToken(
claims, signingKey, audience, issuer, TimeSpan.FromDays(30));
return Ok(new LoginResult()
{
AuthenticationToken = token.RawData,
User = new LoginResultUser { UserId = body.Username }
});
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
private bool IsValidUser(User user)
{
return db.Users.Count(u => u.Username.Equals(user.Username) && u.Password.Equals(user.Password)) > 0;
}
}
public class LoginResult
{
[JsonProperty(PropertyName = "authenticationToken")]
public string AuthenticationToken { get; set; }
[JsonProperty(PropertyName = "user")]
public LoginResultUser User { get; set; }
}
public class LoginResultUser
{
[JsonProperty(PropertyName = "userId")]
public string UserId { get; set; }
}
The actual custom authentication takes place in the IsValidUser function and should link to your existing internal authentication method (do not use the example here, this is for demonstration only)
Custom authentication has to use a client side flow which also meets your requirements.

Related

Custom authentication asp.net core web api

I want to use a secret key (api key) authorization asp.net core web api. The key will be passed in Authorization header like given below,
ex. Authorization keytype;h43484344343bbhfdjfdfhj34343
I want to write a middleware to read this key from request headers and call an internal api to validate the key.
In web api we can write a message handler to do this, but I am new to asp.net core. I'm seeing a lot of samples but they are using inbuilt JWT token authentication. But I wanted to use my own key and I decrypt this key and validate against a database entry.
Can anyone suggest some code samples on how to do this?
I have used this approach in a solution using asp core 1.1. First define a custom scheme:
public static class Authentication
{
public const string Scheme = "Custom";
}
You then have to inherit AuthenticationHandler<TOptions>. Here is where the logic for validating the header value will go:
public class MyAuthenticationHandler : AuthenticationHandler<MyOptions>
{
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var authorizationHeader = Context.Request.Headers["Authorization"];
if (!authorizationHeader.Any())
return Task.FromResult(AuthenticateResult.Skip());
var value = authorizationHeader.ToString();
if (string.IsNullOrWhiteSpace(value))
return Task.FromResult(AuthenticateResult.Skip());
// place logic here to validate the header value (decrypt, call db etc)
var claims = new[]
{
new Claim(System.Security.Claims.ClaimTypes.Name, "Bob")
};
// create a new claims identity and return an AuthenticationTicket
// with the correct scheme
var claimsIdentity = new ClaimsIdentity(claims, Authentication.Scheme);
var ticket = new AuthenticationTicket(new ClaimsPrincipal(claimsIdentity), new AuthenticationProperties(), Authentication.Scheme);
return Task.FromResult(AuthenticateResult.Success(ticket));
}
}
In order to inherit AuthenticationHandler you must create an options class where you set the AuthenticationScheme-property to the scheme you are using:
public class MyOptions : AuthenticationOptions
{
AuthenticationScheme = Authentication.Scheme;
}
After this you have to inherit AuthenticationMiddleware<TOptions>. This will create the handler you implemented in the previous step:
public class MyAuthenticationMiddleware : AuthenticationMiddleware<MyOptions>
{
public MyAuthenticationMiddleware(RequestDelegate next, IOptions<MyOptions> options, ILoggerFactory loggerFactory, UrlEncoder encoder) : base(next, options, loggerFactory, encoder)
{
}
protected override AuthenticationHandler<MyOptions> CreateHandler()
{
return new MyAuthenticationHandler();
}
}
In order to easily plug in your middleware you can define these extension methods:
public static IApplicationBuilder UseMyAuthentication(this IApplicationBuilder app, IConfigurationSection config)
{
return app.UseMyAuthentication(options => {});
}
private static IApplicationBuilder UseMyAuthentication(this IApplicationBuilder app, Action<MyOptions> configure)
{
var options = new MyOptions();
configure?.Invoke(options);
return app.UseMiddleware<MyAuthenticationMiddleware>(new OptionsWrapper<MyOptions>(options));
}
Then in your Startup class you can finally add your middleware:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMyAuthentication(Configuration.GetSection("MyAuthenticationOptions"));
// other stuff
app.UseMvc();
}
Then add the AuthorizeAttribute on your actions specifying the scheme you just created:
[Authorize(ActiveAuthenticationSchemes = Authentication.Scheme)]
public IActionResult Get()
{
// stuff ...
}
There are a lot of steps but hopefully this will get you going!

Adding custom authorize claim based on local database for Azure user .net core

I am trying to recognize database user with Azure AD email address, and then add custom claim to azure AD authenticated user, based on property from local database user. In startup.cs I got:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddScoped<IClaimsTransformer, ClaimsTransformer>();
...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, KayttajatContext context)
{
...
app.UseClaimsTransformation(async (c) =>
{
IClaimsTransformer transformer = c.Context.RequestServices.GetRequiredService<IClaimsTransformer>();
return await transformer.TransformAsync(c);
});
...
}
Then ClaimsTransformer.cs looks like this:
namespace Authtest
{
public class ClaimsTransformer : IClaimsTransformer
{
private readonly KayttajatContext _context;
public ClaimsTransformer(KayttajatContext dbContext)
{
_context = dbContext;
}
public async Task<ClaimsPrincipal> TransformAsync(ClaimsTransformationContext ctrans)
{
string sposti = ((ClaimsIdentity)ctrans.Principal.Identity).Name;
var user = await _context.Henkilöt.FirstOrDefaultAsync(t => t.Sposti == sposti);
if (user.Sposti == sposti)
{
((ClaimsIdentity)ctrans.Principal.Identity).AddClaim(new Claim(ClaimTypes.Role, "Administrator"));
((ClaimsIdentity)ctrans.Principal.Identity).AddClaim(new Claim(ClaimTypes.Role, "User"));
}
else
{
((ClaimsIdentity)ctrans.Principal.Identity).AddClaim(new Claim(ClaimTypes.Role, "User"));
}
return ctrans.Principal;
}
}
}
But this gives me "NullReferenceException: Object reference not set to an instance of an object." at if (user.Sposti == sposti)
If I instead give string value to either one of the variables, if statement works fine. I don't know what I'm doing wrong? Does it have something to do with async? Please help this is driving me nuts.
I was trying to call string sposti = ((ClaimsIdentity)ctrans.Principal.Identity).Name; before it was set. Works now, thanks :)

.net 5 MVC 6 web api using existing identityDb for authentication

I am working on having native app be able to authenticate to a web api which uses an existing identity db database created from MVC6. I understand this is not a secure way of doing things as per this post. However, until I can figure out how to get IdentityServer3 working with a database I thought I would try a simple web api that authenticates to a database I already created when I built a standard MVC 6 web app. Here is what I did:
Created an asp.net 5 web api from the template and added the following:
Settings:
appsettings.json I added:
"Data": {
"DefaultConnection": {
"ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=aspnet5-TestUsers-eaf0c85f-23e4-4603-97ce-b9f49ee1d167;Trusted_Connection=True;MultipleActiveResultSets=true"
}
},
Startup:
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<ApiDbContext>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
Models:
public class AppUser : IdentityUser
{
}
DBContext:
public class ApiDbContext : IdentityDbContext<AppUser>
{
}
Controller:
private readonly UserManager<AppUser> _userManager;
private readonly SignInManager<AppUser> _signInManager;
private readonly ILogger _logger;
...
public async Task<IEnumerable<string>> Post([FromBody]LoginModel model)
{
if (ModelState.IsValid) {
string user = model.userid;
string passwd = model.password;
var result = await _signInManager.PasswordSignInAsync(model.userid, model.password, false, lockoutOnFailure: false);
if (result.Succeeded)
{
_logger.LogInformation(1, "User logged in.");
return new string[] { user };
}
else
{
return new string[] { "Failed" };
}
}
else
{
return new string[] { "Incorrect format received"};
}
}
However, it bombs at the _signInManager line with the error:
System.NullReferenceException: Object reference not set to an instance
of an object.
So apparently _signInManager is Null because I know the model is fine because I am printing the userid and password and they are there.
What am I missing so I can use the signInManager in a web api?
I went back yet another time to see what was different between the web api and the web app, since the web app auth was working fine. Here is what I added to get it working:
controller needed a constructor:
public AuthController(
SignInManager<AppUser> signInManager,
ILoggerFactory loggerFactory)
{
_signInManager = signInManager;
_logger = loggerFactory.CreateLogger<AuthController>();
}
Which got rid of my other error but produced the following error:
System.InvalidOperationException: No authentication handler is
configured to handle the scheme: Microsoft.AspNet.Identity.Application
So after researching that I needed to add to startup:
configureservices:
services.AddIdentity<AppUser, IdentityRole>()
.AddEntityFrameworkStores<ApiDbContext>()
.AddDefaultTokenProviders();
configure:
app.UseIdentity();
Adding these to the above allowed me to post JSON with userid and password.

SignalR in SharePoint

My project has a need for realtime user interaction and I think SignalR will solve my need. I'm technically on a SharePoint 2007 project, although I'm exclusively in application pages and thus barely use SharePoint at all. Regardless, I'm stuck in a 2.0 framework app pool in IIS.
My first approach was to try to create a 4.0 application as a sub-site. Unfortunately, that failed miserably. That approach works in a non-SharePoint world, but it appears that SharePoint has hijacked too much of the request pipeline for this approach to work for me.
So now I'm going down the path of creating a separate IIS Site that's 4.0 and using IIS rewrite rules to fake my app into thinking a particular subdirectory (/realtime/) is local and not a separate site so that I don't have to deal with cross domain request issues. The problem is I can't get IIS rewrite rules to rewrite to another http host (e.g. http://www.mySharepoint.com/_layouts/MySite/realtime/Hello.aspx to http://realtime.mySharePoint.com/Hello.aspx).
Any help with approach #1 or approach #2 or any alternative ideas would be greatly appreciated.
Here is what I did... Web App with signalR .net4.0, then your SharePoint Web App .net 2.
Add this to the global.asax in your Signalr project
RouteTable.Routes.MapHttpHandlerRoute("spproxy","spproxy/{*operation}", new SharePointRProxyHandler());
If you want to raise an event from SharePoint you can do a http POST to this new route URL for example
http://localhost:38262/spproxy
It will pass any posted data onto the httphandler below, that will then broadcast it to your clients.
Here is the code for MapHttpHandlerRoute
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace System.Web.Routing
{
public class HttpHandlerRoute : IRouteHandler
{
private String _virtualPath = null;
private IHttpHandler _handler = null;
public HttpHandlerRoute(String virtualPath)
{
_virtualPath = virtualPath;
}
public HttpHandlerRoute(IHttpHandler handler)
{
_handler = handler;
}
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
IHttpHandler result;
if (_handler == null)
{
result = (IHttpHandler)System.Web.Compilation.BuildManager.CreateInstanceFromVirtualPath(_virtualPath, typeof(IHttpHandler));
}
else
{
result = _handler;
}
return result;
}
}
public static class RoutingExtensions
{
public static void MapHttpHandlerRoute(this RouteCollection routes, string routeName, string routeUrl, string physicalFile, RouteValueDictionary defaults = null, RouteValueDictionary constraints = null)
{
var route = new Route(routeUrl, defaults, constraints, new HttpHandlerRoute(physicalFile));
RouteTable.Routes.Add(routeName, route);
}
public static void MapHttpHandlerRoute(this RouteCollection routes, string routeName, string routeUrl, IHttpHandler handler, RouteValueDictionary defaults = null, RouteValueDictionary constraints = null)
{
var route = new Route(routeUrl, defaults, constraints, new HttpHandlerRoute(handler));
RouteTable.Routes.Add(routeName, route);
}
}
}
Or you could just post directly to a httphandler and get the handler to do a connection.Broadcast
namespace SharePointRProxy
{
/// <summary>
/// Summary description for SharePointRProxyHandler
/// </summary>
public class SharePointRProxyHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
IConnectionManager connectonManager = AspNetHost.DependencyResolver.Resolve<IConnectionManager>();
IConnection connection = connectonManager.GetConnection<MyConnection>();
object payload = null; //Add payload here 'context.Request.Params["data"] ?'
JavaScriptSerializer jss = new JavaScriptSerializer();
var payloadJSON = jss.Serialize(payload);
connection.Broadcast(payloadJSON);
}
public bool IsReusable
{
get
{
return false;
}
}
}
}
You could also use either an event handler calling a .net 4.0 web service or an http handler to grab requests from SharePoint and pass them over to a .net 4.0 application running your signalr code.
You can see an example of using an http handler here: http://spmatt.wordpress.com/2012/04/12/harnessing-signalr-in-sharepoint/

ADAM and Azman with ASP.Net forms authentication

Has anyone been able to make ADAM/Azman work with ASP.Net forms authentication. The default ADAM role provider works only with AD Domain users. And every single article I have read says that you need to write a custom role provider for it.
I have also found out bits and pieces of custom role provider code here and there, but nothing concrete. If someone can share the roleprovider needed for this, that will be great.
I have followed following articles so far :
Custom Role provider (doesn't work) - http://www.codeproject.com/KB/aspnet/active_directory_roles.aspx
Partial Custom Role provider code - http://blogs.msdn.com/b/azman/archive/2006/05/06/591230.aspx
Partial Custom Role provider code again - http://blog.avanadeadvisor.com/blogs/johanr/archive/2009/01/20/12373.aspx
MS Article steps to setup ADAM and use it with ASP.Net (windows auth)
Getting started with ADAM for authentication (no roles) - http://www.alexthissen.nl/blogs/main/archive/2007/07/26/getting-started-with-adam-and-asp-net-2-0.aspx
I have a hacked version, and I seriously mean hacked. I don't need to modify roles in my app, so I only implemented 2 methods. I had to send a username and password to query the directory. Someday I'd like to figure out how to use the ActiveDirectoryMembershipProvider's connection string, but I did not spend a lot of time with it, that would simplify things.
public class ActiveDirectoryFormsRoleProvider : RoleProvider
{
public string DomainController { get; set; }
public string ConnectionLDAPSuffix { get; set; }
public string ConnectionUserName { get; set; }
public string ConnectionPassword { get; set; }
public override string ApplicationName { get; set; }
public override bool IsUserInRole(string username, string roleName)
{
var roles = GetRolesForUser(username);
return roles.Contains(roleName);
}
public override string[] GetRolesForUser(string username)
{
var results = new List<string>();
using (var context = new PrincipalContext(ContextType.Domain, DomainController,ConnectionLDAPSuffix,ConnectionUserName,ConnectionPassword))
{
try
{
var p = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, username);
//looping twice because I was getting AppDomainUnloadedException on 50% of the first attempts
for (var i = 0; i < 2; i++)
{
try
{
var groups = p.GetAuthorizationGroups();
foreach (GroupPrincipal group in groups)
{
var name = group.SamAccountName;
if (!string.IsNullOrWhiteSpace(name))
results.Add(group.SamAccountName);
}
break;
}
catch (AppDomainUnloadedException)
{
}
}
}
catch (Exception ex)
{
throw new ProviderException("Unable to query Active Directory.", ex);
}
}
return results.ToArray();
}
...
For some reason on my production server, I have to make 2 attempts of GetAuthorizationGroups() because 50% of the time the first attempt failed by throwing AppDomainUnloadedException. You might be able to remove that for loop.
And here is my web.config element:
<roleManager enabled="true" defaultProvider="ActiveDirectoryFormsRoleProvider">
<providers>
<clear />
<add name="ActiveDirectoryFormsRoleProvider"
type="myapp.ActiveDirectoryFormsRoleProvider"
applicationName="myapp"
DomainController="domaincontroller.testdomain.corp"
ConnectionLDAPSuffix="DC=testdomain,DC=corp"
ConnectionUsername="username"
ConnectionPassword="password"
/>
</providers>
</roleManager>

Resources