Should I make 3rd party API calls in backend or frontend? - node.js

I have an API and that API needs some data from the Microsft Graph API. I was thinking of implementing an endpoint in my API to refresh the token and use that token to make calls from the frontend. I don't know if that's optimal or safe, hence my question.
EDIT 1: To give a better perspective of what I have, this is the logic I have at the moment. Tell me if this is correct please.
User requests my API's authorization endpoint, which has the Azure's secret key, then the user is redirected to the Microsft oAuth login page. Once logged in oAuth, Microsoft redirects the user to my API, where it saves the JWT tokens in the user's cookies, so the user can refresh the token anytime.
In order to refresh the token, the user simply just makes a call to myapi.com/auth/microsoft/token, where it has the secret key, and it refreshes.

Generally I would recommend always making the 3rd party calls from the back end. It gives you more control and avoids any cross origin complications.
You also want to be aware of any API keys. Most APIs require a key for access and often that key is private and you wouldn't want to share on the front end.
MS Azure APIs have an application and secret token. You cannot expose the secret token to the client. To call directly from the client you would use OAuth to get a JWT token and then you can call from the SPA into the MS Web APIs with that token.
https://learn.microsoft.com/en-us/azure/active-directory/develop/authentication-scenarios#single-page-application-spa
In contrast, there are other 3rd party APIs that are designed to be called only from the front-end. Stripe for example is a payment processing API where the UI can call directly into Stripe and then the client's payment information is never actually passed to the host application, only to Stripe. This improves security.

Related

How to handle multiple IDP with opaque access tokens

I am currently writing a social media backend server, which should handle a lot of users.
As authentication mechanism, we want to use Google's & Apple's OIDC Authentication (Sign in with Apple).
Now, as we get a opaque access token, I cant really imagine a performant way to authorize the opaque access token, as we can not decode the token (not jwt token) and we do not know the issuer.
What I thought:
Authorize the access token sequentially one by one. Meaning:
Fetch Google/userinfo from Google
If 401, Fetch Apple/userinfo
This is unperformant, as the processing time is getting bigger, when we add more IDP's
Save the issuer in the DB and fetch the user's issuer always in the authentication step, before fetching the /userinfo endpoint
This is more performant, but it does not feel right, as the webserver has to make a DB call + HTTP call to authorize a request from the client.
Am I missing a method to get this in a performant way?
BTW: The webserver is a node.js application using express.js
Thank you very much in advice!
The key point here is that foreign access tokens are not designed for authorization in your own back end. Instead you need to issue your own tokens when sign in completes.
AUTHORIZATION SERVER (AS)
The most standard solution is for your apps to talk to an AS that you own, and for it to manage the social logins for you. The AS will then issue tokens that you can fully customize.
This will enable you to fully control the scopes and claims your back end uses for authorization, as well as the session times in UIs. Your back end will only ever work with access tokens issued by the AS, regardless of the login method a user selects.
When you need to add a new login method you just change the AS configuration and will not need to change any code in your apps. A good AS will support integrating with many systems, as demonstrated in this summary page.

Should I use an access token as API-KEY?

BACKGROUND
I have an application with a node backend and an angular frontend. The backend has two GraphQl endpoints:
/auth, which has methods like:
signIn, which authenticate an interactive user (basic usr/pwd) from my angular front and and returns an access token and a secure httpOnly refresh token (cookie);
refreshToken, which returns a new access token; and
signOut, which just revokes the refresh token.
/api, which contains the business rules, and authenticates the request by the access token received (stateless)
The angular frontend authenticates the user by calling the /auth/signIn endpoint, keeps the access token in memory, is unaware about the httpOnly refresh token, and creates a timer to call the /auth/refreshToken periodically.
PROBLEM
I need to grant access to my customers to access the /api programmatically too (e.g. from Zapier), so we are talking about an API-KEY, right? I was thinking about creating an API-KEY section in the SETTINGS area in the frontend and CRUDE methods in the /auth endpoint to maintain them. I would create a new special non interactive “user”, in the users table linked to the generated API-KEY so that, for instance, the user Zapier would be related to the API-KEY created to interact with Zapier and I could see its activity along the other users activities at the audit trail and easily revoke it by deleting that user.
QUESTION
Should I use a long term (?) access token as API-KEY? Wouldn't that defeat the purpose of using access tokens? My /api would no longer be stateless and I would have to check the existence of the access token for each request, right? It doesn’t seem the right choice. Is there a better approach?
Using the refresh token as API-KEY doesn’t seem to be an option to me, first, because it doesn't seem to be allowed to set a httpOnly cookie on the client side, second, because the logic to update the access token would be too complex to the user and third, because I wouldn't want to expose the /auth endpoint.
API Keys are very weak security so ultimately this depends on data sensitivity such as whether it is serious if an attacker can access your data easily for a long time.
I would favour a standard OAuth flow for the type of client - perhaps Client Credentials Grant - so that access tokens are used, which are valid for a limited time period, such as 30 minutes. Expiry and refresh coding is well documented online.
If it is a user app then maybe the app (or its back end) needs to do some work to get tokens correctly.
If the customer is writing code to call the API with code then they need to be given guidance on how to authenticate and manage expiry. These details are well documented online.
Attaching a token could even be managed by an outbound proxy if these users are very non technical, where the proxy deals with supplying a token.

External authentication providers and authenticating requests to RESTful API

I'm working on adding google login to my web app. It's a RESTful app, so once a user is logged in, each individual request must be authenticated with a token.
Currently, I create my own tokens using JWT. I can add useful information to the token object to help with state management.
My question is: once I add google as an authentication provider, do I then need send every request to Google to be authenticated, rather than authenticating it on my own server? Do I then lose the ability to customize the content of the token?
With external authentication providers, is it normal to manage separate JWTs for calls to your RESTful API?
Normally, you'll have the login action use the third party to identify the user. Your internal code will probably create/store/fetch an app local user profile of some sort, and you'll create your JWT based on that. Further calls to your API bearing a valid token are then trusted to have already been authenticated and therefore need no further calls to the auth provider.

Should my app issue it's own access tokens, when using external oauth2 provider (facebook)?

I would like to give the users a possibility to login with some external oauth2 provider (facebook) in my app. The client's part is running on mobile device in a native app.
I am not sure which of the approaches below should I prefer ?
Should the client send the user's access token by facebook with each request ? At each request backend asks facebook to validate the access token. Based on the validation's result, backend performs authorization and return corresponding result to the client.
Should the backend ask facebook to validate the access token only at user logon, then issue its own access token, return the access token back to the client and client will use this access token at making requests to the server to avoid contacting facebook at each request ?
I have read some questions about how to implement the auth with facebook and most of the devs are using B, but I haven't seen any explanation why is it good/bad to use A ?
What I see as benefits of the solutions:
backend doesn't need to care about issuing, refreshing, validating access tokens since this is done only by facebook's authorization servers.
this solution seems to be more effective, since it does not require to connect to facebook at each request.
Security tokens issued by Facebook are signed with a digital signature. The API server only needs access to the public key to validate the signature. There's no need at all to contact Facebook after the user authenticates.
A reason to issue your own tokens after the user signed in with Facebook could be to add claims to the token. But obviously having your own authorization server comes at a cost. It's up to you to weigh the pros and cons.
If you do decide to have your own authorization server, make sure not to write your own! There are open source options like Thinktecture IdentityServer.
I will vote for option B and here is my explanation,
Your API must authorise the request every time with some auth token , which cannot be external provider token, in such case anyone with an access token (eg: other developers) of other provider can access your api, basically there is no auth here.
When your sever issue access token, it's easy to validate and when needed could be revoked easily (eg: on password reset)
While authenticating , your server has fully control over issuing access token , so the validation is made only once and doesn't have to do every time while calling the API.

Manual generating OAuth Access tokens

I am looking to using OAuth to secure some web services. OAuth 2 fits nicely for the use cases I have where the user might access his/her own data using API's or grant access to someone to call API's on his behalf.
However, the initial set of API users are not very technical and they would not want to go through the effort of making API calls just to generate tokens. I am thinking of implementing the following solution but am not sure if this is the right way.
If the user is a developer, then
Have a screen where he/she can register an application. This will generate an API key/secret pair.
To access his/her own data (For 2 legged Auth) have a UI screen where the user can generate a access token for one his registered applications. He can specify the scopes and duration in the form.
If he is a 3rd party developer, then he needs to pass his applications API key to the person on whose behalf he needs to access the API and get an access token in exchange.
If the user wants a another application/developer to access API's on his behalf then
Have a screen where he can enter the third party's API key, scopes and the duration of the authorization. He can pass the generated access token to the developer who'll access the API's
I am going to use same OAuth libraries to generate the token that I would have used if I had gone the web service route. Further, I can also develop services whenever the current situation doesn't scale or the need arises and the existing tokens would still work.
The problem is mainly one of security. By design, duration of access token should not be set by a client. If someone else gets to know the access token and client id during this duration, this user's account will be compromised. Normally this duration is set to be not very long and a second secret value refresh token is used to refresh the current access token. The token refreshing can be automated in code, but in your approach it will need to be done manually.

Resources