Which IUser to Use in AcquireTokenSilentAsync in MSAL - azure-active-directory

I'm asking this question in the context of a mobile (Xamarin) app using Azure AD B2C.
The tl;dr; of this is: Should I always use the IUser obtained from the “sign-in / sign-up” policy when invoking PublicClientApplication.AcquireTokenSilentAsync?
Now let me explain a scenario a bit.
A user tries to use an app, but needs to do a password reset immediately.
Using the MSAL library, the PublicClientApplication.AcquireTokenAsync is called invoking the "reset password" policy.
Assuming a successful call, an IUser object is placed in the PublicClientApplication.Users collection.
In addition, the token returned in the AuthenticationResult from that function is enough to let the user access the resources that need the AD B2C token.
This then means that any future calls to PublicClientApplication.AcquireTokenSilentAsync would use the IUser which was associated with the "reset-password" policy.
It will work ... but is that a best practice?
Or should the app immediately prompt the user to log in, thus invoking the "sign-up/sign-in" policy & thus have the IUser associated with that policy added to the PublicClientApplication.Users collection. Then whenever the AcquireTokenSilentAsync is invoked, that particular IUser can be passed to it? (Instead of the one from the "reset password" policy.)
The same question can be asked whenever an "profile editing" policy is called. A new IUser is added to the collection, and assuming the user has already signed in previous, does it matter which IUser is sent to AcquireTokenSilentAsync?

Matt, I suggest you have a look at the following sample active-directory-b2c-xamarin-native, and it particular the GetUserByPolicy(IEnumerable<User> users, string policy) method located in UserDetailsClient/MainPage.xaml.cs#L76-L85. This method retrieves the right IUser to use given a policy.
foreach (var user in users)
{
string userIdentifier = Base64UrlDecode(user.Identifier.Split('.')[0]);
if (userIdentifier.EndsWith(policy.ToLower())) return user;
}
Its usage is also shown in the OnCallApi method in UserDetailsClient/MainPage.xaml.cs#L116

Related

Implement one general Authorization Service which should be called when I put Authorize attribute on it in multiple applications/APIs

Has anyone an idear what to use as a general Authorization Service and have an working code example or good implementation steps how to implement such of thing.
It takes a lot of time to look what I am after, but didn't found any satisfied solution yet.
IdentityServer is not an option, while my permissions can not be stored as claims, because of the size of the token. It comes with about 200 persmissions, so it should be done in a dbcontext or something.
I looked at the PolicyServer, but it wasn't working as I expected. When I installed it at the IS4 application, it works on the IS4 controllers, but when the Authorize is called from an external application, it doesn't call the Authorize override at all were it should check the permissions.
And it seems that the permissions aren't set in the external application either in the User.Claims or what so ever. I'm missing some settings I think.
What I want to accomplish is that I have one permissions store (table) (which for example contains a bunch of index, add, edit or delete button or what so ever). The should be given to the autheniticated user which is logged in. But this single persmission-store should be available at all applications or APIs I run, so that the Authorize attribute can do his job.
I think it shouldn't be so hard to do, so I'm missing a good working example how to implement something like this and what is working.
Who can help me with this to get this done?
I wrote some code to get the permissions by API call and use that in the IsInRole override. But when I declare it with the Authorize attr, it will not get in the method:
[ApiController]
1) [Authorize]
public class AuthController : ControllerBase
{
private readonly IdentityContext _context;
public AuthController(IdentityContext context)
{
_context = context ?? throw new ArgumentNullException(nameof(context));
}
[HttpGet()]
[Route("api/auth/isinrole")]
public bool IsInRole(string role)
{
2) if (User.FindFirst("sub")?.Value != null)
{
var userID = Guid.Parse(User.FindFirst("sub")?.Value);
if([This is the code that checks if user has role])
return true;
}
return false;
This is the IsInRole override (ClaimsPrincipal.IsInRole override):
public override bool IsInRole(string role)
{
var httpClient = _httpClientFactory.CreateClient("AuthClient");
3) var accessToken = _httpContextAccessor.HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken).Result;
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var request = new HttpRequestMessage(HttpMethod.Get, "/api/auth/isinrole/?id=" + role);
var response = httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).Result;
etc...
This isn't working while it is not sending the access_token in the request
The 'sub' isn't send
Is always null
The open source version of the PolicyServer is a local implementation. All it does is read the permissions from a store (in the sample a config file) and transform them into claims using middleware.
In order to use the permissions you'll have to add this middleware in all projects where you want to use the permissions.
Having local permissions, you can't have conflicts with other resources. E.g. being an admin in api1 doesn't mean you are admin in api2 as well.
But decentralized permissions may be hard to maintain. That's why you probably want a central server for permissions, where the store actually calls the policy server rather than read the permissions from a local config file.
For that you'll need to add a discriminator in order to distinguish between resources. I use scopes, because that's the one thing that both the client and the resource share.
It also keeps the response small, you only have to request the permissions for a certain scope instead of all permissions.
The alternative is to use IdentityServer as-is. But instead of JWT tokens use reference tokens.
The random string is a lot shorter, but requires the client and / or resource to request the permissions by sending the reference token to the IdentityServer. This may be close to how the PolicyServer works, but with less control on the response.
There is an alternative to your solution and that is to use a referense token instead of a JWT-token. A reference token is just an opaque identifier and when a client receives this token, he has go to and look up the real token and details via the backend. The reference token does not contain any information. Its just a lookup identifier that the client can use against IdentiyServer
By using this your tokens will be very small.
Using reference token is just one option available to you.
see the documentation about Reference Tokens

What's the username parameter used for?

I'm trying to authenticate with spotipy using Authorization Code Flow like this:
token = util.prompt_for_user_token(username, scope, client_id=client_id,
client_secret=client_secret,redirect_uri=redirect_url)
When I assign any string to "username", I'm asked to authenticate the request in a browser which is popping up; everything works fine.
When I set a different string to "username" before running my code a second time, the authentication is done against the previously authenticated username (which is still authenticated in the browser session); just as if the value of "username" is not taken into account at all.
Also, I seem to be able to set any arbitrary value to "username" like "pipapo"; when I login to my Spotify account (which isn't "pipapo", obviously) this one is authenticated and methods like current_user_saved_tracks() do get the resources of the account authenticated instead of "pipapo".
Anyways: The access_token and refresh_token are saved to the cachefile .cache-pipapo; thus saving the credentials of the "who-ever-logged-into-the-browser" to the file named after the "wrong" account.
So: What is this parameter good for then, if ultimately the user's interactive selections are responsible for what the code is doing? And why is this even a required parameter if more or less not utilized in the auth process?
I just had a look at spotipy/util.py myself, how def prompt_for_user_token(...) is designed and what parameter "username" is used for; indeed, it is used for defining the caching file name only to hand it over to oauth2.SpotifyOAuth() like so:
cache_path=".cache-" + username
So, you can use any value here; it does not need to be the correct username, necessarily.

How to integrate custom authentication provider into IdentityServer4

Is it possible to somehow extend IdentityServer4 to run custom authentication logic? I have the requirement to validate credentials against a couple of existing custom identity systems and struggle to find an extension point to do so (they use custom protocols).
All of these existing systems have the concept on an API key which the client side knows. The IdentityServer job should now be to validate this API key and also extract some existing claims from the system.
I imagine to do something like this:
POST /connect/token
custom_provider_name=my_custom_provider_1&
custom_provider_api_key=secret_api_key
Then I do my logic to call my_custom_provider_1, validate the API key, get the claims and pass them back to the IdentityServer flow to do the rest.
Is this possible?
I'm assuming you have control over the clients, and the requests they make, so you can make the appropriate calls to your Identity Server.
It is possible to use custom authentication logic, after all that is what the ResourceOwnerPassword flow is all about: the client passes information to the Connect/token endpoint and you write code to decide what that information means and decide whether this is enough to authenticate that client. You'll definitely be going off the beaten track to do what you want though, because convention says that the information the client passes is a username and a password.
In your Startup.ConfigureServices you will need to add your own implementation of an IResourceOwnerPasswordValidator, kind of like this:
services.AddTransient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>();
Then in the ValidateAsync method of that class you can do whatever logic you like to decide whether to set the context.Result to a successful GrantValidationResult, or a failed one. One thing that can help you in that method, is that the ResourceOwnerPasswordValidationContext has access to the raw request. So any custom fields you add into the original call to the connect/token endpoint will be available to you. This is where you could add your custom fields (provider name, api key etc).
Good luck!
EDIT: The above could work, but is really abusing a standard grant/flow. Much better is the approach found by the OP to use the IExtensionGrantValidator interface to roll your own grant type and authentication logic. For example:
Call from client to identity server:
POST /connect/token
grant_type=my_crap_grant&
scope=my_desired_scope&
rhubarb=true&
custard=true&
music=ska
Register your extension grant with DI:
services.AddTransient<IExtensionGrantValidator, MyCrapGrantValidator>();
And implement your grant validator:
public class MyCrapGrantValidator : IExtensionGrantValidator
{
// your custom grant needs a name, used in the Post to /connect/token
public string GrantType => "my_crap_grant";
public async Task ValidateAsync(ExtensionGrantValidationContext context)
{
// Get the values for the data you expect to be used for your custom grant type
var rhubarb = context.Request.Raw.Get("rhubarb");
var custard = context.Request.Raw.Get("custard");
var music = context.Request.Raw.Get("music");
if (string.IsNullOrWhiteSpace(rhubarb)||string.IsNullOrWhiteSpace(custard)||string.IsNullOrWhiteSpace(music)
{
// this request doesn't have the data we'd expect for our grant type
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant);
return Task.FromResult(false);
}
// Do your logic to work out, based on the data provided, whether
// this request is valid or not
if (bool.Parse(rhubarb) && bool.Parse(custard) && music=="ska")
{
// This grant gives access to any client that simply makes a
// request with rhubarb and custard both true, and has music
// equal to ska. You should do better and involve databases and
// other technical things
var sub = "ThisIsNotGoodSub";
context.Result = new GrantValidationResult(sub,"my_crap_grant");
Task.FromResult(0);
}
// Otherwise they're unauthorised
context.Result = new GrantValidationResult(TokenRequestErrors.UnauthorizedClient);
return Task.FromResult(false);
}
}

Securing system-generated nodes in firebase

I've been going through the rules guide but haven't found an answer to this.
App users are able to submit "scores" of different types, which are then processed in JS and written to a "ranking" node. I have it set up so that every time a new score is submitted, the rankings are automatically recalculated and a new child is written if the user doesn't exist or updated if the user exists.
My question is how to secure this "ranking" node. Everyone should be able to read it, nobody except the system should be able to write it. This would prevent people from submitting their own rankings and aggregate scores.
EDIT
This is the operation:
Ref.child('rankings').child(uid).once('value', function (snapshot) {
if (snapshot.exists()) {
snapshot.ref().update(user); //user object created upstream
} else {
var payload = {};
payload[uid] = user;
snapshot.ref().parent().update(payload);
}
});
How would I add custom authentication to this call? Also, since I'm using AngularJS, is there any way to hide this custom token or would I have to route it through a backend server?
The key part of your problem definition is:
only the system should be able to write it.
This requires that you are able to recognize "the system" in your security rules. Since Firebase security is user-based, you'll have to make your "system" into a user. You can do this by either recording the uid from a regular user account or by minting a custom token for your "system".
Once you have that, the security for your ranking node becomes:
".read": true,
".write": "auth.uid == 'thesystem'"
In the above I assume you mint a custom token and specify thesystem as the uid.

How can I pass custom information from an App Engine Authenticator to the Endpoint?

I am referencing #MinWan 's awesome answer in this post Google Cloud Endpoints and user's authentication, where he describes a way to add custom headers to a request against App Engine's Cloud Endpoints.
It becomes clear that we can add a custom header and write an authenticator per each service (e.g. Google, Twitter, Facebook) against which we want to authenicate, where each authenticator reads a specific header and authenticates against the service. If the token is valid, a service typically returns a response with an email address or user id, plus some extra information [A], from which we generate a com.google.api.server.spi.auth.common.User, which is later passed into the endpoint method as com.google.appengine.api.users.User.
First question: Why do we have two different User entities, e.g. users with different namespaces? As it seems, these are neither sub/superclasses, so they are possibly explicitly cast behind the scenes.
Second question: The problem that comes with the explicitly cast User entity and that there is no custom field where I could put the extra information [A] returned by the service, is that the extra information is lost. Such extra information may be helpful for matching the oauth2 user of the external service to a local user or to oauth2 users returned by other services.
Any input? What's the suggested way of handling multiple authentication services?
Just tested, and you can definitely subclass User to contain whichever private fields you want. Just use class inheritance polymorphism to return an object of that type from the Authenticator method, without changing the type from default User in the method signature.
import javax.servlet.http.HttpServletRequest;
import com.google.api.server.spi.auth.common.User;
import com.google.api.server.spi.config.Authenticator;
public class BazUser extends User {
private String secret; // extra piece of data held by this User
public BazUser(String email) {
super(email);
this.secret = "notasecret";
}
public BazUser (String email, String secret) {
super (email);
this.secret = secret;
}
}
public class BazAuthenticator implements Authenticator {
public User authenticate(HttpServletRequest req) {
return new BazUser ("userid#baz.com", "secret");
}
}
Functionally, everything works with:
import com.google.api.server.spi.auth.common.User;
even with gradle:
compile 'com.google.endpoints:endpoints-framework:2.0.0-beta.11'
The IDE warning can be cleared by including #SuppressWarnings("ResourceParameter") as follows:
/**
* Adds a new PmpUser.
*
* #param pmpUser pmpUser object
*/
#SuppressWarnings("ResourceParameter")
#ApiMethod(
name = "pmpUser.post",
path = "pmpUser",
httpMethod = ApiMethod.HttpMethod.POST)
...

Resources