Like the Facebook application, you only enter your credentials when you open the application for the first time. After that, you're automatically signed in every time you open the app. How does one accomplish this?
There's a commom line in all auto-login implementations
Upon an initial login, a token is received and stored on the client side
Upon subsequent visits, if token is available on the client side, the server resolves the identity and logs in automatically
Now concrete implementation variations can be numerous. The token can be a session ID (encripted or not), OAuth token, custom token, username and password should be avoided. Storing token can be on within a browser cookie, browser local storage, can have a server counter-part. Security is the major concern. Generally about the topic you can read more here https://softwareengineering.stackexchange.com/questions/200511/how-to-securely-implement-auto-login
You have an interesting explanation of how does Stackoverflow do it https://meta.stackexchange.com/questions/64260/how-does-sos-new-auto-login-feature-work.
Related
Let me clarify my use case:
I have a next.js application which is a plattform for listing real estate objects. I have several api routes which im using inside my next.js app. for example:
/api/createpost ->
Takes informations from my form on my next.js app and creates a database entry to perform a new post
/api/getposts ->
fetching all the real estate posts from my database and displays it
/api/login ->
logs in a user by checking the credentials in the database and sends a jwt
/api/register ->
registers a user by taking the credentials from a form from my next.js app, registering a user and creating an entry in my database
Now in order to secure my apis I want to make sure to check if there is a valid user session if anybody is calling one of the apis (except the register/login api) to get the expected result. Im doing this by calling the /api/login route and getting a valid user session. Until here everything just works fine. Apis like the /api/createpost can only be called if we have a valid user session.
Now I want to create a mobile app and I want to use my api routes from above to provide full functionality in my mobile app too. It should work the same, if i want to call the /api/createpost on my mobileapp for example, i need a valid user session.
But I want to restrict my api by asking for a key in my database which is pointing to my app and saying okay if you call the /api/createpost api, first of all i need to verify that its the mobile app asking. The mobile app will provide the key in the request then.
I didnt try this yet, but it should work i think. Now the big mess: If we call the /api/createpost and the api wants a valid token to check in the database, which will work for the mobile app, because we are giving it a valid token to check in the database, how can we provide a token if we are calling the api from inside our next.js application? Since I have to do the api call clientside, there is no way for me to provide a secret key or something to validate that the call is coming from my next.js application.
If your application is private
(to be used only by you or a few select people)
You can send a private API key over SSL with each request from your application to the server and verify it. Or you can limit your API to only accept requests from certain IPs.
If your application is public
Unfortunately there's no way to determine where the request is coming from, since anything your app can send, an attacker can send it manually.
Think about it, if your app is trying to make a request to your API, any user can intercept this request before its sent out of his/her machine, and send the exact same request from a different app on the same machine.
You might say, well I can encrypt the requests and responses so that they are of no use to the attacker. But such an encryption will require either a key that's already agreed upon, or some way to provide a new key at the beginning of each session.
If the key is already agreed upon, the app must contain it, as you've already guessed in the question, the attacker can retrieve this key no matter how well you try to hide it.
If the encryption key is a new key provided at the beginning of each session, that's almost how SSL works, your browser handles this transaction. Your server sends a public key to your browser to encrypt the requests which the server can then decrypt with a private key. In this case you've circled back to the same problem, how can you verify to whom you give out an encryption key? What would stop an attacker from requesting the encryption key?
There has to be some way you'd be able to design apps that don't require this restriction. I think the question you should be asking isn't how to restrict your api to a certain app, but how to design apps that don't require this restriction.
We might be able to help you out if you could tell us why you need this restriction.
Update
There is actually a way to verify that requests are coming from your app, but not with an api key.
For Webapps
You can use Google's reCAPTCHA to verify a user on your /register and '/login` routes, and provide an access token or start a valid user session on successful captcha response. With reCAPTCHA v3, you could even verify every user action without interrupting the user. This eliminates both the problems I mentioned in my answer above -
You don't have to store an api key into the app/web app.
The request can't be spoofed as it requires human user interaction within your app. The captcha verification success will arrive to your API from Google's reCAPTCHA server, not from your client app. This communication will be authenticated with a pre-mediated private API key shared by Google to you, which works in the same way as to how you authenticate your external domains.
For Android apps
A similar way to achieve the same thing would be via Android SafetyNet Attestation API. This checks the runtime environment and signs the response with a dynamically generated nonce that your app provides the SafetyNet API.
Please read its docs carefully to understand how you could create potential security loopholes and how to avoid them while using this API.
For iOS apps
DeviceCheck works in a similar way, except the device validity is provided to you by Apple server.
Important edit: "secured" is not the right word here! You cannot tell that a request comes from your app just because the domain is yours. The domain name is not a safe information, as it can be altered easily. See #Mythos comments below.
Initial answer:
Web applications access is secured not based on an API key, but based on a whitelist of domains. That's how we achieve security, because only you have access to the domain where you host your own application: so the request has to be coming from an app you own.
If you try some 3rd party services that provides API for web apps, that's often how they'll work: they will let you configure a set of whitelisted domains that can access your data.
If they provide you an API key, this API key is always meant to be used by a server, not a client-only app.
So if I understand you question correctly, you would do like this for each request:
Check the domain. If it's in the whitelist, perfect, you can keep going. This is meant for web apps (look for "CORS").
If not, check for a valid API token in the headers. This is meant for any app that can store this API token securely (another server for instance, or a mobile app in your scenario though I don't know mobile enough to tell how you store such a key)
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.
I have implemented sign-in to Microsoft to my ASP.NET web application. Everything works as intended, but I am struggling to comprehend how the sessions work. I am using OWIN middleware and OpenID connect.
What is the difference between the SSO-Token and the ID-token? Which
one keeps me logged in?
What happens if I try to access claims ( e.g.
userClaims?.FindFirst(System.Security.Claims.ClaimTypes.sid)?.Value)
from an expired ID token?
How does !Request.IsAuthenticated realize that the current user is
Authenticated after the microsoft login? Is this because The Generic
Identity, is now a Claims Identity, that returns true?
If I am logged in, and keep refreshing the site, at what point will
I be forced to Authenticate again? And what controls this time?
I understand what an ID-token is, and that it carries claims, and how I access and use the claims. I am just confused about how the session works after a user has logged in with their Microsoft account.
There is no SSO token. The id token represents proof of authentication and some basic user info is included in it. So your web app can get name, email etc.
Expiry is based on an auth cookie that the MS libraries issue. This is tied to another token, the refresh token, which represents the session time.
The id token has a digital signature that is cryptographically verified. Also your app supplies a client secret to help ensure that tokens can be trusted.
The cookie is given an expiry related to the refresh token. When the cookie expires the user has to login again.
FOR BETTER UNDERSTANDING
I would strongly recommend tracing messages, via a tool such as Fiddler, as in this blog post of mine.
Personally I prefer Single Page Apps, which only use tokens and not cookies. They make OAuth aspects easier to understand and code can be simpler, though there are still plenty of subtleties.
I am trying to implement WEB API 2 (for Single Page App, not as part of Visual Studio project) OAuth2.0 protocol. As per Which OAuth 2.0 flow should I use, using refresh tokens is not an option. However, I am not sure I understand Implicit Grant flow with eventual Silent Authentication.
Does Implicit Flow mean only issuing normal access tokens? In that case, how do we allow user to stay logged in for long time? How should Silent Authentication endpoint look like, what should it receive and return to client? Is using refresh token really an issue - most of people have their usernames / passwords saved in browser?
Does Implicit Flow mean only issuing normal access tokens? Yes.
In that case, how do we allow user to stay logged in for long time? You can set timeout using "expires_in" parameter.
Refer for Complete Detail here: https://oauth2.thephpleague.com/authorization-server/implicit-grant/
How should Silent Authentication endpoint look like, what should it receive and return to client? Upon authentication of the user during login, the server sends & set the authentication key in the session/browser. So, during every page call, only authentication key is send to server. You shall find many examples of implementation online.
Is using refresh token really an issue - most of people have their usernames / passwords saved in browser? No, it's not an issue. If token expires, you can easily reissue token after authentication. Password & username is not saved in the browser. Only authentication key is stored.
I am using Symfony2.0 and FOSOAuthServerBundle, which implements OAuth2, for managing my APPs clients access to my PHP server.
Everything works perfectly, any token generation, refreshing, etc, etc...
One of the gotten effects is that anytime I enter the APP, I don't need to re-enter my credentials, as the token is still valid or, else, I refresh it using the proper API method.
Typical behavior and all perfect so far.
Now I need to develop a "Logout" button in my APP in order to invalidate that user's token and avoid the use of any refresh_token for him. Sort of revoke his token and/or credentials. In other words, really simulate a Logging Out from the server causing the user to re-enter his credentials next time he gets into the APP.
What OAuth2 sets up for this? Is it a standard behavior with its own API method? Or should I override any behavior in order to getting it?
In case someone's stuck on same thing, I had similar questions, but it turned out to be a conceptual mistake.
Perhaps this may help you:
https://github.com/FriendsOfSymfony/FOSOAuthServerBundle/issues/236
By definition, oAuth2 is STATELESS, so, it does not make sense loging out from an oauth server. To do that, just, destroy the access Token in client side app (We suppose here that you have the control of the app).
But, when a third-party app is connected to your server, you can force the logout mechanism by removing all access tokens that was given by your server to that user of client application. So, when app wants to use one of the destroyed tokens, it will get a 401 HTTP RESPONSE (The access token provided is invalid). Note that if the application has saved the user password in its local storage, it can login automatically to your server without asking the user to enter its password. so, destroying Access Tokens in server side is not a sure method.