How does Blazor Webassembly app know which user has been logged in server side? - blazor-webassembly

I have created a new Blazor WebAssembly project with a separate API project. My WebAssembly project will
query this WebAPI for data.
I have included Identity in my app and this seems to work. I can register a new user and use the below code to log in a user on my Web API.
Code used to log someone in:
var result = await _signInManager.PasswordSignInAsync(model.UserName, model.Password, false, lockoutOnFailure: false);
if (result.Succeeded)
{
//I arrive here, so login was succesful
return true;
}
So Identity seems to be working.
What I'm wondering now is:
my user logs on in the web assembly app
this app uses HttpClient to send a request to my server to authenticate the user, using a username and password
the server checks the username and password and uses _signInManager.PasswordSignInAsync(username, password) which works
How does my client know which user has been authenticated? How can I display more information about this user, check his roles, ...
Does anyone have any guidance or best practices on this please?

Since Blazor WASM is entirely executed on the client, there are two different authentication models: Cookies and Token. (There are more). The basic idea, though, is the same. With each HTTP request sent by HttpClient, an additional header is sent to the server.
Personally, I think Token authentication is a much smarter way supported in different libraries and can be implemented very quickly.
In a nutshell:
With a token-based approach - to be more precise with OpenId Connect -,, you provide an identity service, and your Blazor application will become a client. The token, unlike a cookie, can be read easily by a client. If configured properly, it contains information (claims) like the user's name or email address.
The identity service is an MVC/Razor page application with special libraries handling the authentication flow.
The authentication flow (very, very briefly):
A user wants to login into your Blazor application. If there is no token available, the authentication process starts. The Blazor application redirects the user to the "authentication flow" part of your identity service. It is an endpoint without any "visual" result. The result is another redirect to your MVC/Razor page login view. You can implement any authentication logic here. If the user has been authenticated successfully (based on your logic), the user is redirected back to your Blazor application with a valid token in the URL. This token is extracted and can be used to access an API.
It sounds a bit complicated, and it is a lot to learn and understand. However, you will get a reliable, widely used, and safe way to do authentication and authorization.
As a starting point for reading
https://identityserver4.readthedocs.io/en/latest/quickstarts/0_overview.html
https://identityserver4.readthedocs.io/en/latest/topics/signin.html#login-workflow
https://learn.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/standalone-with-authentication-library?view=aspnetcore-5.0

Related

IdentityServer4 Web Login via id_token or access_token

Context
I'm building a hybrid native/web app. Part of it will be API driven, and part of it will be webviews showing an existing website.
Once logged in with my OIDC SDK (Amplify/AppAuth), I can access the user's id_token and access_token.
However, because the webviews used in the SDK are not controllable, I cannot reuse the cookies generated by my identity server.
Question
For the reason above, I'm trying to login a user from their id_token or their access_token. (both are JWTs)
I believe this isn't supported by IdentityServer4, but I am looking to implement something myself.
What I have found so far:
1) id_token_hint
OpenID Connect Core 1.0, Section 3.1.2.1, id_token_hint parameter:
OPTIONAL. ID Token previously issued by the Authorization Server being passed as a hint about the End-User's current or past authenticated session with the Client. If the End-User identified by the ID Token is logged in or is logged in by the request, then the Authorization Server returns a positive response; otherwise, it SHOULD return an error, such as login_required. When possible, an id_token_hint SHOULD be present when prompt=none is used and an invalid_request error MAY be returned if it is not; however, the server SHOULD respond successfully when possible, even if it is not present. The Authorization Server need not be listed as an audience of the ID Token when it is used as an id_token_hint value.
If the ID Token received by the RP from the OP is encrypted, to use it as an id_token_hint, the Client MUST decrypt the signed ID Token contained within the encrypted ID Token. The Client MAY re-encrypt the signed ID token to the Authentication Server using a key that enables the server to decrypt the ID Token, and use the re-encrypted ID token as the id_token_hint value.
According to this optional spec of OpenID, I should be able to use the id_token on the /authorize endpoint to login the user. I know this isn't implemented in IdentityServer4, but I'm looking at AddCustomAuthorizeRequestValidator to do that. However I'm not sure how to "get a user from their id_token" in the code. Do you have any pointers?
2) using AddJwtBearerClientAuthentication
This method sounds like I could authenticate from my access_token, but I can't find much documentation on how to use it.
THE PROBLEM
Let me know if I am misunderstanding requirements, but it feels like you have 2 different apps and are trying to make them feel like a single integrated set of screens.
The default behaviour will be as follows since web views are private browser sessions and cannot use system browser cookies. You want to prevent the second step since it is a poor user experience:
User signs in to the mobile app
Whenever a secured web view is invoked, there is a new private browser session with no auth cookie, therefore requiring a new login
COMMON APPROACH: SUPPLY A TOKEN TO THE WEB VIEW
It can be common to forward the mobile token to the web view, if the two apps are part of the same user experience. If required, extend the mobile app's scopes to include those for the web app.
You should not need to issue any new tokens or change the security model of either app. But the mobile and web apps need to work together on a solution.
POSSIBLE WEB VIEW SOLUTION FOR A SERVER SIDE WEB APP
This might be the simplest option:
Provide new .Net Core web back end entry point URLs that require tokens instead of cookies
The mobile app could then call those endpoints from web views, and supply its own token in the Authorization Header of the web view request
The web back end could then forward mobile web view requests to your Web APIs
The code to add the Authorization Header to a web view request may be a little tricky but there are ways to do it:
Android
iOS
POSSIBLE WEB VIEW SOLUTION FOR AN SPA
An option that works for Cookieless SPAs is for the web view to ask the mobile host app for an access token via a Javascript bridge. Some code of mine does this for a cookieless SPA:
Web UI Code to Call the Mobile Host
Android Code
iOS Code
You can clone / run the mobile github repos from your local PC to see the solution. I will be writing up some notes on this in my blog's Mobile Web Integration Post.

Angular & Asp.Net Core - Enable 2 Factor Authentication

I have a single page application written in angular 7 which communicates with my ASP.Net Core 2.2 Web API server.
On login, the user sends his credentials to my authorization server (connect/token) using "resource owner password credentials" grant.
I am trying to add 2-factor authentication (SMS), but I can't find any example describing how to do this. All the examples I found were written with MVC using cookies authentication.
I was thinking about this flow but it feels to me there should be a much better way
A user enters his user name & password
If the user has 2 factors enabled I will send him an SMS with a code. In addition, a limited access_token and id token will be sent to the client. this access_token will be valid only to enable the user to send the 2-factor code.
if id-token will have a claim for 'two factors': 'on', I will redirect the user to an SMS confirmations code.
The user will send a post request with the code. If the code matche, I will return to the client a new access_token with all the claims.
2FA is a very bad fit for the resource owner password credentials grant (at least, not the standard flavor).
An interactive flow like the code or implicit flows will allow to easily implement that using ASP.NET Core Identity and its default controllers/Razor pages, in a completely standard way.

Thinktecture Identity Server 3 authorization flow for WebApi-Angularjs Web App

I have a question regarding the best standard architecture of Authorization in web application that is written in Asp.Net Web Api on the backend and and has an angularjs client side.
According to what I had seen before, the "Resource Owner Credentials" flow is what would be used in such cases, where the webapp would send the user's credentials to the server and obtain access token (and refresh token) and then using an interceptor, every call to the backend apis would contain the access token in the header.
However, I have recently seen arguments about it being a bad idea, as it gives the user's credentials to the client app.
What is the best flow for a scenario when you have javascript client directly calling you WebApis? What is the best way to secure it using Identity Server?
You could also consider implicit flow or hybrid flow, when the client app (angular) is redirecting the user to login on the openid identity provider (Identity Server) an upon a successful authentication, this returns an access/identity token which can be used in subsequent calls to the Apis.
In this case the client app is never touching client credentials, it always has to manage tokens.
see also https://www.scottbrady91.com/OpenID-Connect/OpenID-Connect-Flows

How should I share authentication from a desktop application to a web application using OAuth 2.0

I have a RESTful API written in ASP.Net that implements OAuth 2 for authentication, and it's currently accessed through a web application. I've also got a legacy desktop client that accesses the same resources directly (not through the RESTful API and without OAuth, but using the same login credentials and hitting the same database). The requirement I'm trying to meet right now is to allow a user to click a link in the desktop application in order to open the web app to a specific screen, and when they do, to have the web app authenticate automatically so that they don't have to manually log into it (since they've already logged into the desktop app).
I'm trying to work out how I can handle this within the constraints of the framework. I'm not too familiar with OAuth 2 in general, but from what I understand I shouldn't share tokens between clients and there are no flows specifically for this kind of hand-off (unless I'm missing something). Worst case scenario, I could generate a temporary token outside of OAuth that's used by the web client to authenticate rather than a username and password, but I'm hoping to avoid stepping outside of what's already in the framework to do what I need to do.
So the question is this: is there some decent way built into the OAuth 2.0 framework to handle this sort of "handshake" between two applications, or should I just build my own method of dealing with it?
Using temporary one-time tokens is actually part of OAuth spec (authorization_code grant type). In this case this short-lived code can be exchanged for access_token (and refresh_token). You will have to implemenent generating and validating of this authorization_code.
If you are using OWIN OAuth middleware:
You can generate the code at separate API endpoint accessed by your desktop client app.
After receiving token, pass it to your browser and direct it to auth endpoint with grant_type=authorization_code over secure connection. Example: call Process.Start("https://example.com/ExternalLogin/authorization_code_goes_here"). At the webpage redirect user to your OAuth Token endpoint with grant_type=authorization_code.
AuthenticationTokenProvider.Receive will be called, in which you will validate your token. (Example code here).
After successful validation OAuthAuthorizationServerProvider.GrantAuthorizationCode will be called, in which you will process the authenticated user in the same way you process it with grant_type=password.
Remember that your token validation logic should ensure that your tokens are short-lived, usable only once and transmitted over secure connection.
This is sometimes called "single sign-on" if you want to research this topic further.

Authenticating ASP.NET web app with WCF service

Background
I have an ASP.NET web application that interacts with WCF services. The web application and the WCF services are under my control. The ASP.NET web application uses a custom implementation of the ASP.NET Membership Provider Model (with passwords stored in hashed form) to authenticate users who log in to the web application. Both the ASP.NET web application and WCF services have access to the same membership database.
Since the users will supply their password only once, and I don't want to have to store their passwords anywhere or annoy them by repeatedly asking them to resupply their password, I need an appropriate mechanism to authenticate the users with the WCF services.
Based on other questions and answers I have seen, I am considering a sort of "login session" approach, where a login session will be created in the custom membership database when the user initially logs in to the web application, with the login session identified by a GUID, and automatically expired after a period of inactivity. The login session GUID will be "remembered" by the web application for each logged in user (either stored in the Forms Authentication Ticket or in the session).
The WCF service will also provide its own login operation accepting a user name and password and returning a login session GUID as described above.
The WCF service would then accept the login session GUID for all other operations, and verify that the GUID represents a valid login session that has not expired before allowing the operation to proceed.
I have done quite a bit of background reading on this, and there is a lot of material on straightforward use of the UserName client credential type, but this would require the web application to remember the user's password, which doesn't seem like a great idea to me.
I've done some research and found material on MSDN, but this seems like a lot of effort to implement what (to me at least) seems like a pretty common usage scenario.
How to: Create a Custom Token
Question
Is the general approach of the "login session" described above reasonable?
If so, what is the best way to achieve it?
If not, can you suggest an alternative?
This is a very reasonable approach.
To do this you setup your service endpoint and configure it with your custom membership provider (You can do the same with SQL membership provider, it doesn't require a custom one).
On the web application you set up the Authenticate event of the Login control to instantiate a new service proxy and set the username/password in the ClientCredentials in the proxy.
Now when you make the call to the Service through the proxy WCF will pass these credentials through the secure channel to the service and use them for authentication.
Now you simply need to store the proxy in session and use it for future access to the service as it has the channel state and a private key.
protected void LoginControl_Authenticate(object sender, AuthenticateEventArgs e)
{
bool Authenticated = false;
try
{
MyServiceClient proxy = new MyServiceClient("MyServiceEndpoint");
proxy.ClientCredentials.UserName.UserName = LoginControl.UserName;
proxy.ClientCredentials.UserName.Password = LoginControl.Password;
//It doesn't really matter what is called or what it does because
//Membership Provider for the Service does the authentication.
string retval = proxy.login("Logging in");
//Now that channel is established the proxy needs to be kept
//since it contains the channel state which includes a private key
Session["MyServiceProxy"] = proxy;
Authenticated = true;
}
catch (Exception ex)
{
//Login Error...
}
e.Authenticated = Authenticated;
}
There are two possible solutions I can think of:
Firstly, if the WCF service is an internal service, the web application can then send the name of the user that is asking for the data with each request.
The second is that you store the user name and hash of the password (or actual password) somewhere. Either in the session state or in the user cookie (a session cookie stored in memory passed to the user over https). Then pass the user name and password to the WCF service with each request.
See my answer on Storing password in forms authentication cookie - ASP.NET and WCF calls for a solution that does not require storing passwords.
Thanks to everyone for your input. I've settled on an approach (at least for now). It's fairly simple and works well for my purposes.
Using the "UserName" clientCredentialType, and an explicit service login method that returns a security token (token generation details omitted for brevity), the service client can decide whether to pass the genuine password as the password property on the client credentials or the security token instead (obtained from the login method). If the client is a web application the security token could be stored in the forms authentication ticket, or the session, or wherever.
Using the "Custom" userNamePasswordValidationMode and a custom implementation of UserNamePasswordValidator, the validator inspects the password to determine whether it is a valid security token or a password - if it's a password the client credentials are authenticated against the user store (SQL Server database), and if it's a security token then it is checked to ensure it is valid, hasn't expired, and belongs to the user name.
I would suggest to take a look at Geneva which is aimed at solving scenarios as yours. The basic idea is to require the same security token, by mean of an HttpModule, for both the WCF services and the ASP site. The token will be release after authenticating against your membership database and may contain useful info (claims) on the user.
For an intro you may read Bustamante's article.
Microsoft has a WCF service that you can use to authenticate users with ASP.NET Membership.
The code is actually built into the framework - you just need to create a .svc file to use it.
http://msdn.microsoft.com/en-us/library/bb398990.aspx

Resources