I am trying to figure out correct way to implement JWT auth with Blazor (WASM).
After going through the docs a got an idea on how the built in components work but still the whole picture is not clear to me.
So in my scenario i have an API sever that will be used, the API server can issue JWT tokens and they can be used to authenticate against the endpoints where required.
So right now i am trying to figure out the correct role for each component.
For start we have AuthenticationStateProvider, as i understand this component has the responsibility of obtaining the JWT token either from server or if one stored locally, also it could handle token refresh when required?
Now since i will be using Typed HTTP Clients i will be using IHttpClientFactory, along with that i will have AuthorizationMessageHandler to attach tokens to desired HTTP Client instances.
Things fall apart for me when i am trying to deal with IAccessTokenProvider, as i understand the default implementation will be called once a HTTP Client is created and http request is about to be made.
What is not clear is how this IAccessTokenProvider will obtain the token.
So the question is whether i should create my own implementation of IAccessTokenProvider and if so how it should handle the tokens.
As i said i wont be using any built in authentication providers and will have my own JWT auth system instead.
Thanks.
The first three paragraphs are very clear and correct. This is how you should do that. I can post here some code snippet to demonstrate how it is done in practice...
Things fall apart for me when i am trying to deal with IAccessTokenProvider,
No wonder... The IAccessTokenProvider is not relevant here. The IAccessTokenProvider is a token provider used in the new JWT token authentication system for WebAssembly Blazor App. But if you want to implement the JWT authentication yourself, you must do that as you've described in the first three paragraphs... which I can summarize like this:
When a user makes a first access to a protected web api endpoint and he's not authenticated (or registered), he's redirected to the relevant pages, type his credentials, etc, which you pass to your Web Api end point dedicated to authenticate the user (register if necessary, etc.), after which the action method called produce the JWT token, and send it back to the WebAssembly Blazor App running on the browser. You should store the JWT Token (perhaps in the local store), and retrieve it whenever you perform HTTP calls (Adding the JWT Token to the headers of the request).
The above described process also involve the implementation of the AuthenticationStateProvider object, that is updated with the authentication state, and notifies subscribers, such as the CascadingAuthenticationState, that the authentication state has changed, at the end of which process other components and objects adapt themselves to the new situation... you know, re-rendering, etc.
So, you see, you've received a JWT Token from your Web Api, stored it a local store, read it, and use it. Reading the Jwt Token from your local store and parsing it, to great extent, is something that the IAccessTokenProvider does, but in the new authentication system, and as you do not use this system, the IAccessTokenProvider is not relevant.
What about automatic Token injection in headers of HTTP client, can i or should i still investigate custom AuthorizationMessageHandler or this component would not be usable without IAccessTokenProvider?
You may add your Jwt Token to each HTTP call as demonstrated below:
#code {
Customer[] customers;
protected override async Task OnInitializedAsync()
{
// Read the token from the local storage
var token = await TokenProvider.GetTokenAsync();
customers = await Http.GetFromJsonAsync<Customer[]>(
"api/customers",
new AuthenticationHeaderValue("Bearer", token));
}
}
which is perfectly fine. But of course you can create a custom DelegatingHandler modeled after the AuthorizationMessageHandler or still better the BaseAddressAuthorizationMessageHandler as you're going to use the IHttpClientFactory to provide your HttpClient service. Try first to attempt to use them without any modifications, and if it's not practical just emulate their functionality.
The last things that bothers me is the implementation of obtaining access token and storing it locally.The best approach i can think of so far is to have a global authentication service, this service will provide the functionality of obtaining the token, refreshing it, storing etc. Both IAccessTokenProvider and AuthenticationStateProvider will use it when token is requested plus will be notified whenever authentication state changes like user logs in or out.
Perfect... Note: The AuthenticationStateProvider should be notified of the change in the status of the Jwt Token. As for instance, when you get a new token from your Web Api endpoint, your code should add it to the local store, and then notify the CUSTOM AuthenticationStateProvider of the change. Your code also should notify the AuthenticationStateProvider in case you delete a Jwt Token, so that your user interface will reflect this changes, etc.
Good luck.
Hope this helps...
Related
I have an API developed in .NET 5.0 in which I implemented Swagger.
Right now I have a endpoint in which the user obtain a JWT Token with a user and password and this token is used to authorize into swagger like below:
Authorize
Everything works fine to this point.
But now I want to implement and endpoint in which the user has to introduce this token to authorize swagger in order to use the API.
It is possible have these two validation? I want the user authorize through the button if they use the website but if they use postman (or another API) have to use the authorize endpoint.
I have searched to try this implementation and I found IOperationFilter but I'm not sure if its what I need
I don't have a clear idea about this, but if I understand what you want to do correctly, you want to remove the security measures in requests made through the website.
While doing this, you can create a public token or define a guest or similar role and perform the transactions that occur on the website as if you are playing this guest role.
But finally, I must say that your request will cause a major security vulnerability. Also, keep in mind that every transaction performed on the website can be easily imitated by tools such as postman. This will completely override your other security measures.
In a WASM Blazor application I am injecting the Http client by doing this in the top:
#inject HttpClient Http
Now I want to be able to set a username and password like in regular .NET, there you would do:
handler.Credentials = new NetworkCredential(userName, password);
However, I cannot find any property of Http where I can set my credentials, how can I set it?
It's not clear what you do as you provide almost no code to demonstrate your issue. However, it is very clear what you're striving at.
In order to authenticate and authorize users in Blazor WebAssembly App you can use Jwt authentication or OpenID connect that pass a user's credentials to a Web Api end points to verify the users, create a Jwt token, and pass it back to the front end where you can store the Jwt Token in the local storage, and retrieve it when the user logs in, accesses various resources, etc.
Note also that the HttpClient is not really the actual HttpClient. It is based on the JavaScript Fetch Api, and it is missing features like WebSockets, etc.
I've posted in this section answers about how to use Jwt Authentication and OpenID Connection. You'll have to search for those answers dealing with Jwt Authentication, as it was relatively long time ago, and I don't remember their locations. However, here is the links to Adding OpenID Connect to IdentityServer4, and Accessing token from Blazor
Hope this helps...
I've created a new Blazor app using the Visual Studio template adding B2C using the wizard during the project creation.
All works great with authentication. I can sign in using my favorite identity provider and receive the id_token. I don't know where I can get the id_token, but I can see the claims in my user identity.
What I need help with is how do I take the information in the claims to acquire the access token. I need this token to call my endpoints.
This seems like it should be straightforward and a common thing, but I can't seem to find any good examples.
In a normal MVC app, I can get this through the ConfidentialClientApplicationBuilder.AcquireTokenByAuthorizationCode but that doesn't work in Blazor (unless I'm doing something wrong).
I've tried this: https://learn.microsoft.com/en-us/azure/active-directory-b2c/active-directory-b2c-access-tokens
I think it'll work. How do I get "code"(id_token) in my blazor app? It's what is decoded behind the scenes and all I can find are the claims resulting from decoding the "code".
I've used the azure's "Run user flow" to access an example id_token("code") and pasted it into my project and made the call in the link above and it seems to work. I'm running into permission issues, but it's at least a successful call.
Maybe if I can just get access to the id_token I can make it work from there?
This seems like a client side app, similar to a SPA. In which case you must use the implicit flow, where the response type is “id_token token”, and returns an id token and access token to the browser in one call. This isn’t an exact answer, but only our MSAL.js library can make this type of call, but Blazor seems to use C#, and msal .net does not do client side auth calls. acquireTokenByAuthCode() would work client side as long as you register the app as a native app, so a secret is not required.
So i am experimenting with client side blazor and trying to figure correct implementation for JWT or Cookie authentication.
Suppose that i have a service that serves the tokens and refresh token on client side i will have multiple API Services that will need to use this token or cookies to create API requests.
So as i understand i will need to implement and API Service classes similarly as documented here https://learn.microsoft.com/en-us/dotnet/standard/microservices-architecture/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests
So in order to have a consistent authentication state an Authentication service should be introduced and injected to the API Service classes. This service will have the responsibility of doing auth,getting initial token and doing a refresh token when required.
So with the latest release of Blazor there is this new component called AuthenticationStateProvider, the use of it seem to be clear but the question is would it be correct approach to use this as a global Authentication service?
So HttpClientFactory is not supported on the client. In client-side Blazor, HttpClient is added to the DI system, and you can inject it into your components or classes.
So you want to use AuthenticationStateProvider as global Authentication service ? I'm not familiar with this component, but I'm afraid, from the short read I had after reading your question, that it is not an authentication system, but a tool to provide authentication state information, such as user age, etc. You still need to configure the Jwt middle ware, create a controller responsible for issuing the Jwt token, and so on. And I guess that you can employ the AuthenticationStateProvider to provide your client app with authentication state before a user, say, logged in and after, etc. I do hope I'm not wrong about it, at least not entirely.
So hope this helps...
When using JWTs on unsecure clients like SPAs, it's a bad practice to implement refresh tokens, as those are meant to be private.
The current OIDC recommendation (for SPAs) is to implement the Authentication Code Grant strategy.
I would recommend you to take a look at Blazor-Auth0 library (author here), it implements the Authentication Code Grant strategy blacked on Auth0, so you don't need to reinvent the wheel (and it's free).
Even if you don't want to add a new dependency, you would find it as a good source of examples of how to implement JWT authentication and authorization in Blazor.
https://github.com/henalbrod/Blazor.Auth0
To give some background, bear with me,
We have an existing external OAuth service that is used for shared authentication across all of our apps. It has a login form that is used for user auth and provides JWTs for its APIs, which are called in the app I'm currently working on.
The app I'm working is using .NET Core 2.0 with a React front end. The server side is basically responsible managing the token for the external APIs, and providing 'proxy' APIs for the React client to call, which then in turn call the external APIs. Essentially the architecture is like any other standard Web API/MVC app, but instead of grabbing data from a database directly, it's just calling some other external APIs.
In the context for the .NET Core app, it is using JWT authentication for it's APIs. Once the user has authenticated with that external service, they will have a JWT for the external APIs (stored in session) and another JWT for the internal 'proxy' APIs (passed to the client in a cookie). So the client makes a call ("api/users"), that gets routed to my controller, authorized using JWT, that controller makes a call to some UserService, which uses the JWT stored in the session to make a call to the external API.
React client makes API call with token --> .NET Core API grabs JWT stored in session --> uses JWT to make call to external API
This all works fine, but now I'm running into some issues with refreshing the client JWT. I'm currently passing just the an access token to the client in a cookie, but I need to also pass a refresh token to the client so that it can grab a new access token when the expiration time has passed, which I'm not currently doing. I also need to somehow pass the expiration time of the token as well. Does it make sense to serialize all of this into a JSON object and pass that in a cookie? What would be a good way to get all of this to the client? I'm essentially trying to get to a point where the client will check, "Has my token expired yet?", if yes, use the refresh token to get a new one, if not, continue with the API call.
Sorry if all of this information is an overkill to a simple question, just trying to give some context.
If you are using Identity Server, you can check the refresh tokens here