How do I get the requested url from my MembershipProvider class? - c#

I'm currently trying to implement the MembershipProvider class, but my user repository isn't typical to most of the examples I'm finding on the net. Instead of retrieving a user, we simply check the user's identity against an auth server for the requested URL:
User attempts to "POST" to ws.example.com/jobA
Attribute validates the user with the auth server to see if they have access to this action/url
Rejects or accepts the request
A couple other posts pointed me in this direction for implementing a custom membership provider. The way I figure it, in order to make this work, I need to be able to see what the requested URL was, and be able to look at their cookies. In Filters, i have access to the HttpRequestMessage. How do I get at the info i need in this context??

[Despite this thread is not specifically about SharePoint Membership Providers, I will just leave this solution here because I couldn't find it anywhere else, and kept being redirect to this thread while looking for solutions on Google. I hope it helps someone]
For SharePoint Membership Providers, we have the Security Token Service, which is a WCF Service. In this case, System.Web.HttpContext.Current is always null, but you can get the actual Request URL using this property:
System.ServiceModel.OperationContext.Current.RequestContext

Related

Keycloak set custom in access_token claim via API

I need to set a custom claim in the access_token from within a C# application. Is there a way to achive this?
So that I can create custom access_tokens on the fly.
I read though the Keycloak API reference but wan not able to find a solution.
I need this because I have a User that, depending on the application state, should get access to different ressources. I dont want to create different user to achive this. I do not want to save information into the cookies to achive this. And I also do not want to save information in URL to achive this.
I already tried to use a uma-ticket token for this as described here. But all i got was this error:
{
"error": "invalid_grant",
"error_description": "Invalid bearer token"
}
The most common option is to implement dynamic behaviour via claims. At the time of token issuance, the authorization server can reach out to an API endpoint (or database), to send account attributes and receive back custom attributes.
In Keycloak you need to use a protocol mapper for this. The last time I looked you had to develop one in Java, then configure it in the Admin UI for your client app. There is a worked example here.
This is usually a better design than trying to issue new user level access tokens on the fly. Eg an access token contains the important values used for authorization, such as role=manager or subscription_level=gold, so that the claims are trusted. The resources they grant access to could then vary a little based on runtime conditions.

C# and ASP.NET Core 6 : authentication and user details in "session"

I'm going to get so many "okay grandpa" comments for this one.
I've read a dozen articles and every SO question I could find on this subject.
I must have been away too long or missed something completely, because I swear that user authentication used to be very simple. I seem to recall built-in methods and a session on the server simply knowing who the user was via a cookie or similar, with the ability to store information "in session". I don't recall even setting up authentication in years past, it was just built-in to new applications.
Instead, the most succinct guide I could find is very involved. I think I need a token authorization/authentication setup because there may be consumers (like apps) who don't have a typical cookie pattern these days. In my head, the token works like a cookie except it's manually held on the user end and passed via header with each request?
To its credit, the guide worked, at least for logging in and correctly utilizing the simple Authorize attribute in controllers. However, User.Identity.Name is always empty, even when User.Identity.IsAuthenticated is true, which is perplexing.
How I think auth is working:
User request hits API with username/password
Service checks the combination, and returns an encrypted JWT to the user
The user sends the JWT back with every request
The server decrypts this JWT to identify the user - this is probably where I'm wrong
So here is where my question comes in:
I need more data about the user, like access to the entire UserModel with every request, but I don't want to go to the database to find it every time. This is where I think there should just be a session object in memory, but that doesn't appear to be the case with token authentication.
TL;DR:
Where do I put user-specific, short-term ("session") information for consumption in future requests where a user is identified with a JWT in the Authorization header instead of a cookie?
Session state isn't right, because it's hard-wired to a cookie
HttpContext.Items aren't right, because it's just for the one request
Cache storage isn't right, because it's not user/session specific. I could potentially create a session-like user-keyed storage here but that seems way, way over-engineered for this.
Basically anything where I'm passing all the data (not just a user identifier) to the client then relying on the client to pass it back seems wrong? But feel free to correct me.
The server decrypts this JWT to identify the user This is probably
where I'm wrong
The JWT token is not encrypted, its signed so you can't alter it. You can open it if you look at jwt.io for example.
Where do I put user-specific, short-term ("session") information for
consumption in future requests where a user is identified with a JWT
in the Authorization header instead of a cookie?
You put it in the principle claims of the token. In the guide you linked it wrote:
var claims = new List<Claim>
{
new Claim(JwtRegisteredClaimNames.NameId, user.UserName)
};
So you add whatever you want to the claims to store it on the token and later you can access this data via:
var claim = _contextAccessor.HttpContext.User?.Claims.FirstOrDefault(d =>
d.Type == ClaimTypes.NameIdentifier);
You also can't use any of these other examples that you listed like HttpContext.Items because those are not signed. If the token is altered in any way the system identifies this and returns a 401

IdentityServer4 - Use different user stores based on client_id

I am creating an identity service using IdentityServer4 and AspNetCore.Identity. The service will serve multiple websites which need to have separate users. As such I was thinking the simplest way to keep users separate is to have separate user collections in my database (in my case MongoDb), and simply use a different one based on the client_id.
I am registering my IUserRepository like so:
services.AddSingleton<IUserRepository>(x => new UserRepository(mongoClient));
I was thinking the easiest way to achieve what I want is to handle it at the DI level, so I have come up with so far is to change this to:
services.AddScoped<IUserRepository>(x => GetUserRepo(x, mongoClient));
Where GetUserRepo is a method that reads the client_id out of the request and returns a repository for the correct user collection.
This does seem to work but my issue is how "hacky" the implementation of GetUserRepo has to be:
I have to check if the request url is /connect/token and if it is, manually read the request body to find the client_id
If the user is passing a Bearer token (my identity service also includes a management API for creating users etc) then I have to manually decode the token and find the client_id claim
Really I am just wondering if there is a nicer way to do this (anything provided by IdentityServer4?) and also if this is "ok" to do at all?
The only thing I have found that could maybe help is IIdentityServerInteractionService.GetAuthorizationContextAsync however this requires a returnUrl which I don't have as I am using client credentials or resource owner password credentials methods to authenticate.
Not specifically an answer but as it might help put others on the right track:
After thinking more carefully about our scenario I realised it wasn't really necessary to do what I was trying to do. Instead each website will simply use AspNetCore.Identity to handle their own users (with their own data stores) and I am just using IdentityServer to secure my API and set each website up as a client.

Asp.Net Core 2.2 - Understanding Authentication Middleware and External Logins

I have been trying to wrap my head around this concept but have many questions and unfortunately, all official documents and tutorials are based on Visual Studio templates with individual user accounts.
My goal is pretty straightforward I believe. I have a web application which will only support external provider logins (namely: Facebook, Twitter, and LinkedIn). I do not want to support cookie authentication since there won't be a support for custom username/password.
My first problem is to define a default AuthenticationScheme. Below is my startup.cs:
services.AddAuthentication()
.AddFacebook(/* options */)
.AddTwitter(/* options */)
If I define a controller action with Authorize attribute I get no default authentication scheme defined error when I hit that route. However, I want users to be redirected to my login route if they are unauthorized. If I modify startup.cs like below it all works but then I think I support cookie (old forms authentication?) authentication which I don't want to.
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie()
.AddFacebook(/* options */)
My other issue is that I don't know what happens under the hood of AddFacebook() call. If I set up my middleware this way and log in with Facebook I magically get all the necessary tokens, claims and suddenly I have an application cookie set and my fb_login callback route can access to Facebook's token! When I check the network requests I see there is a hit to the signin-facebook route -which I didn't define- and I guess under the hood it calls HttpContext.SignInAsync() etc... but if I refresh my fb-login callback and check if
HttpContext.AuthenticateAsync(FacebookDefaults.AuthenticationScheme)
returns Success = true no! it returns false! But it was true just one second ago?
Also, when should I be using methods like AuthenticateAsync() and SignInAsync()?
Long story short I need a tutorial or documentation that explains this middleware without asp.net Identity framework, EntityFramework and templates.
I want to understand how a simple AddFacebook() call binds everything, and if I want to manually do that (say with AddOauth) how can I achieve the same functionality?
I'm not a fan of "automagically working" code so if someone can explain what's going on here I'd be very appreciated.
Cookie auth is used to persist the authenticated state between the requests. There is no substitute for this, and no it's not the same as forms auth, though cookies are used in both cases. The reason for that is simply that cookies are what makes state work over the HTTP protocol, which is itself stateless. If exclude the use of cookies, then there is no other mechanism to maintain state.
Using something like the Facebook auth scheme directly authorizes the initial request, but again, because there is no state, the next request is no longer authenticated without going through the Facebook OAuth flow again.
Long and short, the other auth schemes are there for things like APIs, where each request is typically authenticated individually via something like the Authorization header. A web browser doesn't work this way, and instead relies on cookies handle subsequent authorization. No cookies, no auth.

Web Api: How do I allow only my mobile app and website to connect? [duplicate]

A common use case for WebAPI would be to have shell views rendered by MVC controllers, which contain javascript that then hit your API to access data.
But let's say you have some expensive API operations and you don't want people remotely accessing those endpoints -- you only want your MVC views, delivered by your application, to access them. How could you go about protecting them?
In this case Request.IsLocal doesn't work, because javascript is invoking it from the client's browser on their machine. Even if it did work, you need to dig to get the real HttpContext in order to find this property -- and that solution wouldn't work in self-hosted WebAPI.
For API endpoints that require a valid IPrincipal, you could protect them with the [Authorize] attribute. But what about API endpoints that you want your app to be able to access for anonymous users?
I have tried a solution and will post it separately as an answer, because I'm not sure if it's the best (or even a good) approach.
If your MVC site uses authentication, you could enable forms authentication for your Web API methods. You could write a custom [Authorize] attribute that will check for the presence of a forms authentication cookie which will be sent from the AJAX call and if present construct the principal.
Another possible solution is to protect your API with tokens which is a more RESTful style. The idea here is that when a user authenticates on your MVC website you could generate and pass a token to the view which will be used when sending the AJAX request to the Web API which in turn will verify the validity of the token and its signature.
If on the other hand your site doesn't use authentication, then things will get very complicated because you have no way of knowing whether the request comes from a trusted client since you are using javascript to call your API methods.
Before you go harping about "what have you tried", here is what I have tried. It works. Just not sure if there is a better way.
Create an MVC action filter and add it as a global filter during Application_Start.
Create an Http (WebAPI) action filter and use it on actions that should reject remote requests.
The global MVC filter does this:
Looks for a specific cookie in the request. If the cookie is there, its value is decrypted. The decrypted value should be a string representation of a DateTime, so use DateTime.TryParse to get it out. If the value is correctly parsed to a DateTime, and that DateTime is less than a day old, STOP HERE and do nothing else.
If the cookie is not there, or cannot be decrypted / parsed, or is older than a day, write a new cookie to the browser. Use the current DateTime.UtcNow.ToString() as the value, encrypt it, and write it with HttpOnly = false.
The WebAPI filter does this:
Looks for a specific cookie in the request. If the cookie is there, decrypt its value and try to parse it out as a DateTime.
If the value is a valid DateTime and is less than 2 days old, STOP HERE and do nothing else.
Otherwise, throw a 403 Forbidden exception.
A couple of notes about my current implementation of this. First of all, I use AES encryption with a shared secret and a salt. The shared secret is stored as an appSetting in web.config. For the salt, I enabled anonymous identification and used Request.AnonymousID as the salt. I'm not entirely fond of the salt because it's tricker to get at in a WebAPI controller, but not impossible as long as it is not self-hosted.

Categories

Resources