What's the pattern for sharing a RestAPI token? - c#

Is there a pattern for how should I store and reuse a restAPI authorisation token across multiple classes?
I'm consuming a RestAPI, my login code takes a user, password and server and then returns an auth token. That login code sits in a common base class for all my RestAPI calls which works fine. But each new object doesn't know the token so has to reauthorise. I need them to share the same token once it's been generated. I can't use a singleton as I may have to login with multiple different users in the same session.
I'm sure there's a pattern for that but I can't find it, can you help?

What you need is a cache. The login service can be a singleton that cache access tokens, you can use a concurrent dictionary to implement the access tokens cache.
Something like this:
public class LoginService
{
private ConcurrentDictionary<string, string> accessTokenCache = new ConcurrentDictionary<string, string>();
private string callServerLogin(string user, string password)
{
throw new NotImplementedException();
}
public string Login(string user, string password)
{
var accessToken = callServerLogin(user, password);
accessTokenCache[user] = accessToken;
return accessToken;
}
public bool TryGetCachedAccessToken(string user, out string accessToken )
{
return this.accessTokenCache.TryGetValue(user, out accessToken);
}
}

Related

C# ASP.NET: How to automatically decode my bearer token for each method?

I have a Web API that uses bearer tokens for authentication and authorization. The presence of a token signifies that the user is authenticated; the claims within that token specify the authorizations the user has.
I wrote an OAuth class that queries the database upon user login to determine the user's permissions. All of this works fine.
Now, for each method in each controller, I need to extract information from the bearer token's claims. What I have done now is to define a class that contains the entities I need from the token and write a static method to take a User object from within a controller method and produce an instance of my class containing the token data:
// Bearer token class
class TokenData
{
public string UserId { get; set; }
public int GroupId { get; set; }
public string UserTag { get; set; }
// ... more properties as needed
}
// Method to get bearer token data from user object
internal static TokenData getTokenDataFromUserPrincipal(System.Security.Principal.IPrincipal p)
{
var identity = (ClaimsIdentity)p.Identity;
IEnumerable<Claim> claims = identity.Claims;
var enumerable = claims as Claim[] ?? claims.ToArray();
var claimTypes = from x in enumerable select x.Type;
var types = claimTypes as string[] ?? claimTypes.ToArray();
if (!types.Contains("userId")) return null; // if a token was given but it has no UserID it's not valid.
var claimsByType = enumerable.ToDictionary(x => x.Type, x => x.Value);
TokenData td = new TokenData
{
UserId = claimsByType["userId"],
GroupId = types.Contains("groupId") ? int.Parse(claimsByType["groupId"]) : 0,
UserTag = types.Contains("userTag") ? claimsByType["userTag"]) : null,
// more properies as needed
};
return td;
}
// A controller method that uses the claims
public HttpResponseMessage DoSomething()
{
TokenData td = getTokenDataFromUserPrincipal(User);
// Now I can access the token data in the td variable.
return Request.CreateResponse(td.UserId);
}
Ok, so this all works perfectly. The thing I'm looking for though is if there's a way to automate pulling the claims from the User object whenever a controller method is invoked.
The Web API architecture already basically does this with the User object itself - it contains any information that is part of the request related to the user. (Similar to the Request object, which contains all of the HTTP request data.)
What I'd like to be able to do is, instead of calling getTokenDataFromUserPrincipal at the start of each controller, to instead just have a static variable available similar to how Web API does it - e.g. TokenData. For example:
// A controller method that uses the claims and doesn't have to explicitly retrieve them.
public HttpResponseMessage DoSomething()
{
return Request.CreateResponse(TokenData.UserId);
}
I do know how to use attributes - I actually wrote custom attributes which pull the same token data and use it to determine if a user can access a certain class of function - so for example I can just include [MinimumUserLevel(2)] in front of my controller methods. But having to add an attribute to each method simply moves the problem outside of the method.
To sum: Is it possible to have another static variable, scoped at the request level, that user code can populate per request without having to copy code to the beginning of each controller method?
Ideally, there would be a way to insert a function in the pipeline, so that prior to the controller method being run, I can run the code to get the token data from the principal, so it will be ready when the controller method runs. (Note that the method to pull token data simply returns null if the data doesn't exist - this is the expected behavior for this static variable in the instance of a call with no token.)
You can either manually verify the jwt token. Or add the JwtBearerAuthentication to the Owin stack.
Here is one of the found samples. This would parse the token for each request and set the User properties of each request.
You would have to set it up for the secret you were using when you created the jwt token.
The provided sample validates and converts the JWT token to a ClaimsIdentity which is set on each request and is accessible through the User.Identity property within each controller. No custom code is necessary for that.
And since this package is provided by Microsoft, it might be better then some self made JWT parser.
(User.Identity as ClaimsIdentity).FindFirst("userId")?.Value to access your user ID.
You could also extend the IIdentity with the following code.
public static class IIdentityExtensions
{
public static string UserId(this IIdentity identity)
{
return identity.GetClaimValue("userId");
}
public static string GetClaimValue(this IIdentity identity, string claimType)
{
return identity
.AsClaimsIdentity()
.Claims.FirstOrDefault(c => c.Type == claimType)?.Value;
}
private static ClaimsIdentity AsClaimsIdentity(this IIdentity identity)
{
if (!(identity?.IsAuthenticated ?? false))
throw new UnauthorizedAccessException("User not logged-in");
return identity as ClaimsIdentity;
}
}
And then accessing the user id like User.Identity.UserId()
The solution to this turned out to be simpler than I was making it: define a subclass of ApiController.
So I simply wrote a class that looks like this:
public class MyApiController : ApiController
{
// Makes token data available to any method on a class deriving from MyApiController
public TokenData Token { get; set; }
// Override constructor - parse token whenever instance is created
public MyApiController()
{
// Extract the token data from the User variable (User is pulled in from the base ApiController)
Token = getTokenDataFromUserPrincipal(User);
}
private static TokenData getTokenDataFromUserPrincipal(System.Security.Principal.IPrincipal p)
{
...
}
}
Now I have all of my controllers derive from MyApiController rather than from ApiController. From that point, I'm able to access the Token variable in any controller's logic, which will contain the parsed token data.

How am I supposed to configure Jwt Bearer authentication?

I am trying to make an web application that uses Jwt token authentication, logging on ect works fine.
Now I'm trying to add authorisation on who can access what pages and so on. On my controller I added [Authorize]. But when I'm logged in and try to go to one of the actions I always get a blank page and when I inspect it the action always returns 401 Unauthorized, any suggestions?
My headerservice for sending the authorization header to the Api:
using System.Collections.Generic;
namespace RelationizeWeb.Facade.Services
{
public class HeaderService : IHeaderService
{
private const string AuthorizationHeaderKey = "Authorization";
private const string BearerHeaderValue = "Bearer";
public Dictionary<string, List<string>> CreateAuthorizationHeader(string token)
{
var dict = new Dictionary<string, List<string>>
{
{ AuthorizationHeaderKey, new List<string>{ $"{BearerHeaderValue} {token}" } }
};
return dict;
}
public Dictionary<string, List<string>> CreateHeader(string key, string value)
{
var dict = new Dictionary<string, List<string>>
{
{ key, new List<string> { value } }
};
return dict;
}
}
}
Example of an Api request we make in for example our OpinionMakerService:
public IEnumerable<OpinionMaker> GetOpinionMakers(JwtTokenResponse jwt)
{
try
{
return (List<OpinionMaker>)_relationizeApiAgent.GetOpinionMakersWithHttpMessages(_headerService.CreateAuthorizationHeader(jwt.AccessToken)).Body;
}
catch(Exception)
{
return null;
}
}
You can use azure to generate the JWT for your application . You need to call an extension method named UseJwtBearerAuthentication in the StartUp.cs file for the REST api and use [Authorize] attribute to the controllers . The UseJwtBearerAuthentication method works like below
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
Authority = <your auth name>,
Audience = <target audience URL>
});
Please see the links below for more reference
1.https://blogs.msdn.microsoft.com/webdev/2017/04/06/jwt-validation-and-authorization-in-asp-net-core/
https://pioneercode.com/post/authentication-in-an-asp-dot-net-core-api-part-3-json-web-token
you can use a client side library like ADAL.JS to call the azure service to obtain a JWT bearer token and then send the token in each request to controller. It will be verified in each request. We followed this approach in an Angular2 app with Azure JWT authentication.
Link for adal.js
https://github.com/AzureAD/azure-activedirectory-library-for-js
Resource :
https://blogs.msdn.microsoft.com/premier_developer/2017/04/26/using-adal-with-angular2/
edit : you can use the below code to access JWT Token and add it to the Authorization header before making a call to controller from another controller
HttpClient client = new HttpClient();
var token = <add your token here>; // call GetToken() method here and extract access_token from JwtTokenResponse class property.
client.DefaultRequestHeaders.Authorization = new
AuthenticationHeaderValue("Bearer", token.Substring("Bearer ".Length).Trim());
//call the api method using SendAsync() or PostAsyc() etc.

Want to use service account to connect to AD under which my app is running

In my MVC-5 application I am adding/editing users in active directory through my application. Whenever I make changes in AD, each time I need to pass ldapUserName(Admin) and ldapPassword(pass#123) to connect to Active directory. Then only I can perform operation in AD. In place of passing credentials each time I would like to use Service account (domain\service_account) under which my application in running in order to connect to AD. How to achieve that ??
Simply creating the DirectoryEntry by new DirectoryEntry(ldabPath, null, null) will use credential of current user (service account in case of Windows service). The key is passing null to both username and password.
Is that what you want?
Just make sure the service account has enough permission to set password for all users involved.
You can choose one of 2 options.
Option 1. You run your application on a computer which is joined to a domain, where you add\remove users from. In this case the simplest solution is to run your application (application pool) under the domain account with sufficient permissions (in simple way can be an account that belongs to Domain Admins group. Will be also an admin on the host, where application runs)
Option 2. You run your application on a standalone computer, that is not joined to a domain. In this case you can choose one of the following:
A. Impersonate your thread to act as the domain account when performing all network connections. You need to use LogonUser function with LOGON32_LOGON_NEW_CREDENTIALS flag. The drawback of this method is that all network connections (e. g. you connect to a network share) will be made under domain account. For more details on implementation, see this post.
B. Create a connection manager which will create DirectoryEntry for you with required credentials. See the code below:
public interface IDirectoryEntryManager
{
DirectoryEntry GetDirectoryEntry(string domain, string baseDn);
}
public interface ICredentialProvider
{
Credential GetCredential(string domain);
}
public class Credential
{
public string UserName { get; set; }
public string Password { get; set; }
}
public class DirectoryEntryManager : IDirectoryEntryManager, IDisposable
{
private class DomainConnectionInfo
{
internal DomainConnectionInfo(string server, Credential credential)
{
Server = server;
Credential = credential;
}
internal string Server { get; private set; }
internal Credential Credential { get; private set; }
}
private bool disposed;
ICredentialProvider _credentialProvider;
Dictionary<string, DomainConnectionInfo> connectionsInfo = new Dictionary<string, DomainConnectionInfo>(StringComparer.OrdinalIgnoreCase);
Dictionary<string, DirectoryEntry> connections = new Dictionary<string, DirectoryEntry>(StringComparer.OrdinalIgnoreCase);
public DirectoryEntryManager(ICredentialProvider credentialProvider)
{
_credentialProvider = credentialProvider;
}
public DirectoryEntry GetDirectoryEntry(string domain, string baseDn)
{
if (disposed)
{
throw new ObjectDisposedException(this.GetType().Name);
}
return GetOrCreateConnection(domain, baseDn);
}
public void Dispose()
{
if (!disposed)
{
foreach (var connection in connections)
{
connection.Value.Dispose();
}
connections.Clear();
disposed = true;
}
}
private DirectoryEntry GetOrCreateConnection(string domain, string baseDn)
{
DomainConnectionInfo info;
if (!connectionsInfo.TryGetValue(domain, out info))
{
var credential = _credentialProvider.GetCredential(domain);
var dc = DomainController.FindOne(new DirectoryContext(DirectoryContextType.Domain, credential.UserName, credential.Password));
info = new DomainConnectionInfo(dc.Name, credential);
// maintaining a connection to rootDse object to make all LDAP queries use this single connection under the hood. Increasing performance
var entry = new DirectoryEntry(string.Format("LDAP://{0}/RootDSE", dc.Name));
entry.RefreshCache();
connections.Add(domain, entry);
connectionsInfo.Add(domain, info);
}
return new DirectoryEntry(string.Format("LDAP://{0}/{1}", info.Server, baseDn), info.Credential.UserName, info.Credential.Password);
}
}
Didn't test the code. Use server bind instead of serverless bind (e. g. LDAP://domain.com) is better in case if you create a user in one part of program and try to access it in another part. Using serverless bind you can connect to different DCs, so the user you are trying to access may be not replicated to 2nd DC.
Be aware that the domain controller may become unavailable, so you need to implement logic, to search for another DC and refresh your connections cache if required.
You may store credential in a file\LSA possibly encrypted and make DirectoryEntryManager class singleton.

Call ASP.NET from c# (win32 based) client

I've got a web application created with ASP.NET and a windows native client program written in c#.
The windows native program needs to send and fetch data from the ASP.NET web application.
I guess in the web application I'll need a controller for the external calls. And in the client Software I somehow Need to call them.
Is there a way to achieve calls with complex data types (lists of classes) as parameters?
How do I secure the calls from the client? Simple http-logon?
for example I'd like to transfer an instance of this class to or from the ASP.NET web application:
public class Address
{
public String Street {get;set;}
public String City {get;set;}
}
public class CustomerInformation
{
public String No {get;set;}
public String Name {get;set;}
public List<Address> Addresses {get;set;}
}
Of course the Windows client is running somewhere local while the ASP.NET Service is running in the web.
I would add API controller and put some methods there. For instance
// Addresses API
public class AddressController : ApiController
{
private readonly IRepository<Address> _repository;
public AddressController(IRepository<Address> repository)
{
_repository = repository;
}
[BasicAuthorize]
public IList<Address> GetList()
{
return _repository.GetAll();
}
}
// Constomer information API
public class CustomerInformationController : ApiController
{
private readonly IRepository<CustomerInformation> _repository;
public CustomerInformationController(IRepository<CustomerInformation> repository)
{
_repository = repository;
}
[BasicAuthorize]
public IList<CustomerInformation> GetList()
{
return _repository.GetAll();
}
}
To secure those methods you can use Basic authentication. This means that you can add authorization header for each request:
For example how it looks for user "myuser" with password "test"
Authorization: basic bXl1c2VyOnRlc3Q=
// Custom attribute for Basic authentication
public class BasicAuthorizeAttribute : System.Web.Http.AuthorizeAttribute
{
private readonly string[] _permissionNames;
public BasicAuthorizeAttribute()
{
}
public BasicAuthorizeAttribute(params string[] permissionNames)
{
_permissionNames = permissionNames;
}
protected override bool IsAuthorized(HttpActionContext actionContext)
{
// check if user has been already authorized
if (base.IsAuthorized(actionContext))
return true;
var user = AuthenticateUser(actionContext);
// here you can check roles and permissions
return user != null;
}
private IUser AuthenticateUser(HttpActionContext context)
{
var request = context.Request;
AuthenticationHeaderValue authHeader = request.Headers.Authorization;
if (authHeader != null)
{
// RFC 2617 sec 1.2, "scheme" name is case-insensitive
if (authHeader.Scheme.Equals("basic", StringComparison.OrdinalIgnoreCase) && authHeader.Parameter != null)
return AuthenticateUser(authHeader.Parameter);
}
return null;
}
private IUser AuthenticateUser(string credentials)
{
try
{
// parse values
var encoding = Encoding.GetEncoding("iso-8859-1");
credentials = encoding.GetString(Convert.FromBase64String(credentials));
var credentialsArray = credentials.Split(':');
var username = credentialsArray[0];
var password = credentialsArray[1];
// authentication
var membershipService = new IMembershipService();
return membershipService.ValidateUser(username, password);
}
catch (Exception)
{
// Credentials were not formatted correctly.
return null;
}
}
}
On client side you can use HttpClient to send async request
public async Task<Address[]> GetAddresses() {
var client = new HttpClient {BaseAddress = new Uri(_settingsService.GetHost())};
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var base64 = Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(string.Format("{0}:{1}", "myuser", "test")));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",base64);
HttpResponseMessage response = await client.GetAsync("api/addresses");
if (response.StatusCode != HttpStatusCode.OK)
throw new Exception(response.ReasonPhrase);
string content = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<Address[]>(content);
}
Is there a way to achieve calls with complex data types (lists of classes) as parameters?
Yes, The server application as ASP.NET or ASP.NET MVC or (preferably) ASP.NET WEB API can provide services with complex data types. In fact there is no limitation in declaring methods.
How do I secure the calls from the client? Simple http-logon?
There are wide ranage of authentication and authorization mechanism in ASP.NET (MVC, WEB API) which give you opportunity to choose one them.
The data transfers between your client and server via XML or JSON.
The "WebClient" class provides everything that you need to make a call from client to server.
More information:
http://www.codeproject.com/Articles/33798/HTTP-GET-with-NET-WebClient
How to post data to specific URL using WebClient in C#
How do I log into a site with WebClient?

How to implement ASP Identity IPasswordStore if I don't have hashed password stored

So I have a MVC5 project with ASP Identity, I don't have direct access to user database instead I was provided with web services, for example:
bool register (string username, string password, string email) //password is plain text
bool login (string username, string password)
My problem is that I have no idea how to implement IUserPasswordStore because ASP Identity force me to use Hash while I'm not allowed to store hashed password because this web services will be used not just by me.
My UserStore.cs:
public Task<string> GetPasswordHashAsync(TUser user)
{
//I'm suppose to provide hashed password here, but since I don't have any hashed password stored, I have no idea how to implement this method
string passwordHash = userTable.GetPassword(user.Id);
return Task.FromResult<string>(passwordHash);
}
public Task<bool> HasPasswordAsync(TUser user)
{
var hasPassword = !string.IsNullOrEmpty(userTable.GetPassword(user.Id));
return Task.FromResult<bool>(Boolean.Parse(hasPassword.ToString()));
}
public Task SetPasswordHashAsync(TUser user, string passwordHash)
{
//do nothing since I don't need to hash the password
return Task.FromResult<Object>(null);
}
Well your application doesn't have a password store, don't implement this.
Override the PasswordSignInAsync method in the SignInManager, and have it call your web service.

Categories