How to configure loopback-component-oauth2 to save token in cookies? - loopback

I'm trying to configure loopback-component-oauth2 to save access token in cookies, but can not find out how to do this. By default, access token is adding to '/auth/callback' route as part of hash. Example path:
http://localhost:3000/auth/callback#access_token=0bzaFOmVY6ICeArhzCGso27Cwc07d7k4&expires_in=1209600&token_type=Bearer
I saw in logger, that we can try to get access token after "Generating access token" stage, but I still do not understand, how to get it.
image

Related

Persisting user state in sveltekit

I'm trying to hook up a Strapi backend to a SvelteKit frontend, and stuck on how to persist user login state so that everything doesn't just reset on refresh, or when navigating to a new page. I've tried:
Storing the jwt and user object issued by Strapi in localStorage and initializing the Svelte store with it. Seemed like I was getting close, but a) I couldn't do export const user = writable(localStorage.user) because that code was running in the browser, and I couldn't wrap it in an if (browser) {...} because import and export can only appear at the top level. Also tried a function in hooks.js to read the contents of localStorage and update the store, but it seems that functions getting called from there run on the server, even if it's the same function that works to access localStorage on login... and plus b) from what I gather, storing jwt's in localStorage is insecure.
Storing the jwt and user object in an http only cookie. Cookies and http headers seem really confusing, and I had a hard time manipulating them to store the jwt and put it in each header. But I think what really stumped me was essentially the same SSR issue of never knowing essentially how to successfully interface between the client and server. I.e. if (browser) {...} never seemed to work, or I couldn't get it to, anyway. (Happy to provide more code details on what I tried here if needed. It's a mess, but it's saved in git.)
I know this is a thing every app that has users needs to do, so I'm sure there's a way to do it in SvelteKit. But I can't find anything online that explains it, and I can't figure it out from the official docs either.
So am I missing something easy? (Probably.) Or is there a tricky way to do this?
For a SvelteKit SPA authenticating to Strapi, here's the happy path flow I would use:
SvelteKit page /routes/login.svelte collects the username (identifier) and password and a button's on:click handler posts those values via fetch to your own /routes/auth.js:post() endpoint. Why? Because now your server's endpoint is handling the login on behalf of the user so you can set an httpOnly cookie in the response.
In your auth.js endpoint post() method, you will want to do a few things:
Use fetch to post identifier and password to Strapi authentication (http://localhost:1337/auth/local) to get the JWT (response.body.jwt)
Use fetch to get user info from Strapi (http://localhost:1337/users/me). In the get, add a header called 'Authentication' with a value of 'Bearer ' + the JWT you just received from Strapi in the previous step
Return the user info from the response from Strapi and pass it back to your client in the response.body
Set a header httpOnly cookie with the value of the JWT.
Back on the client in the last part of the login button's on:click handler, take the user info from res.json() and put it in a writeable store called user or in the SvelteKit session store...
import { session } from '$app/stores'
...
const loginClickHandler = async () => {
...
const fromEndpoint = await res.json()
session.set({ user: fromEndpoint.user })
}
At this point, you have the user information persisted client-side and the JWT as an httpOnly cookie that can't be read or modified by client-side JavaScript code. Each request you make to your own server's pages or endpoints will send the JWT cookie along.
If you want to logout, call an endpoint on your server (/auth/logout) that sets the existing jwt cookie to have an Expires based on the current date/time:
response.headers['Set-Cookie'] = `jwt=; Path=/; HttpOnly; Expires=${new Date().toUTCString()}`
You would also want to clear the user object in your store (or the session store).
The main takeaway for the above example is that your client would never directly talk to Strapi. Strapi's API would be called only by your SvelteKit server's endpoints. The httpOnly jwt cookie representing your session with Strapi would be included in every request to your server's endpoints to use/validate with Strapi's API (or delete if expired or the user logged out).
There are lots of other approaches but I prefer this one for security reasons.

Problems getting started--Http 403

I'm trying to access the SmartSheets REST API as described in the "Getting Started" documentation here: https://smartsheet-platform.github.io/api-docs/#getting-started and elsewhere. I generated an access token in the UI and, using Postman, tried a couple of simple GET requests cribbed from the documentation:
https://api.smartsheet.com/2.0/users/me
https://api.smartsheet.com/2.0/sheets
I set the Authorization and Content-Type headers as indicated. In both cases, I get Http 403-Forbidden errors with the message "You are not authorized to perform this action."
So how do I get authorized to perform these (or any other) actions?
You might want to verify that your access token value is correct. Also, when you set your Authorization header, are you including "Bearer " before your access token?
In Postman, it should look something like this:
Just a typo. I was including "Bearer" in the authorization header, but I had 2 spaces between "Bearer"and the token. You can only have one.

How do I get an access token from Google Analytics using a service account?

I need to do a POST request to get the access token from Google Analytics using a service account.
I need to bypass manual authorisation in a browser, so I have used a service account for which I have all the details, private_key, client_id, etc
https://www.googleapis.com/oauth2/v4/token&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion={PRIVATEKEY}
If I do the above, I receive not found.
Can anyone help?
Cheers
The Server Side Authorization Embed API demo has an example of doing this. The example uses the Google APIs client library for Python, but, as #DalmTo mentions in their comment, you could look at what it's doing under the hood to replicate the requests.
Here's what the code that gets the access token looks like:
import json
from oauth2client.client import SignedJwtAssertionCredentials
# The scope for the OAuth2 request.
SCOPE = 'https://www.googleapis.com/auth/analytics.readonly'
# The location of the key file with the key data.
KEY_FILEPATH = 'path/to/json-key.json'
# Load the key file's private data.
with open(KEY_FILEPATH) as key_file:
_key_data = json.load(key_file)
# Construct a credentials objects from the key data and OAuth2 scope.
_credentials = SignedJwtAssertionCredentials(
_key_data['client_email'], _key_data['private_key'], SCOPE)
# Defines a method to get an access token from the credentials object.
# The access token is automatically refreshed if it has expired.
def get_access_token():
return _credentials.get_access_token().access_token

Facebook Login: Is there a way to receive URL fragment in redirect as querystring parameter?

So when using Facebook login, it turns out that if you set response_type to token or code%20token then the "response data is included as a URL fragment" on the URL that redirects to your app. Seriously, why? When would that ever be useful to anyone?
Anyway, I'm hoping that this is somehow security related and that it's there for a reason other than to annoy people, but I would otherwise like to know if there's a way to disable it, since I'm using Angular and it's a real pain to handle urls with hashbangs.
If I set a response_type of code only, the code is returned as a nice and clean query parameter, but I would like to receive both in order to perform additional validations. Is there a way to change this behaviour? Thanks.
EDIT:
Well it turns out that setting a response_type of code returns #/= at the end of the url, so there's no way to get a clean querystring. It has already been established that there is no way to change this behaviour, but I'm still interested in finding out why Facebook is doing it. Is it security related? What is the purpose of these url fragments?
Answers
To your first question, I imagine you would use token when you are handling everything in-browser and not processing at the server.
To your main question, as far as I know, you can not change how Facebook redirects successful authorizations. Facebook doesn't give you the token as a clean query parameter. If you use token or code token, what you want will be in the hash fragment. Sorry.
NOTE: This may not be a problem for you. I'm not sure what you want with "additional validations", but when you use code token, exchanging the resulting code gets you a different token than the one you just got embedded in the URI. Both are now valid and will expire separately. Really, you probably need either the code or the token; both won't help you since they're not linked.
Examples of using all three methods:
If you set response_type to code you get redirected to something like:
http://example.com/redirect_uri?state=thestatepassed&code=AQDN9E9GYjA8NbyCt
87_jV5vHnCQylNxmBswo6Z1BsrR7lmTPom6wjrzfan6P4GBLDt3EQrfPg0xSLoMLxBBfscsyfSY
JNM2vu9OoqEQXXSJCTUq_fMpUwqkYbCHp-GAqL4H1ymbMz7zPKAG61V9BtKTSuez39yhawOu7l-
6ww4thP41Ka9PVcknTQ6fPjPXKYSyxEmANps9zevCPFsXpBZCO7_dms65-ZZuG2wVBd16gFnBZH
q8EY0qih6-9o61wXh7bBvVPVSZ2im7Oj1nx47YgDpbD3X0XdlVhUoGYmBdER9hNmIC2PmmY7VAo
PlYCZc#_-_
From there, you need to exchange the code through Facebook's OAuth endpoint to receive an access token. The access token can then be used against the Facebook APIs.
If you set response_type to token you get redirected to something like:
http://example.com/redirect_uri#access_token=CAACYnSxGEhsBAJBg0ohZBhAf7pKEU
sm5ytZAZBzKjISFuRun2ZByZCqEsxrVIgtiO7iIlJZBBbGm6fRPQXItZCX6YgjPknUBsr78tJtv
W6fySULrUo9vdW57ZCMUUIlNaeZAcU8DzUXKmFpgotOyhE3jXYz1c3eu00Aii0AZBsPrtrwjpwQ
mV8VYQNiqKZBIsqOrIwZD&expires_in=4168&state=thestatepassed
You have everything you need to call the Facebook APIs. The access token returned is valid, but should probably be checked against Facebook's token inspection endpoint if you're doing something server-side. (Really, at that point, just use code. I've never done it this way, so good luck.)
As you noted, the access token is now contained within the hash fragment of the url.
If you set response_type to code token you get redirected to something like:
http://example.com/redirect_uri#code=AQAtzsjPivFPsJ538KFlPuhLaK6pDMlrGDiwmi
KDcpgNfWrO1EdX5i6zK_Op2D0QDEXZLyifXxh4TSeBZCWhnkl7YV1LMyEkbPURAWSoqRoeG7tfM
4nB4nDAHOK0H9umb0KnoypRT1pP05FJKhl2QjpCJrPPFDHl6y-1X9ZMj1uVHtmPNi4tG_6QAbuL
RaGadBkekb22uJ0iwSrWc9OKi6ET70lCTYb18hbwUkzHXtTq12nNEdsDJ7Ku2wEBwMygFwErYDX
CrnPoFoah_z0UPCfv3XZLy98Dhlzw_lnx8nnCB-PCppOWRqmydvQJehPd86k&access_token=C
AACYnSxGEhsBALXHRQwfm4UoauRlZBJDVpZCiM6ZCuM3bE965F5JVBfBB8inTFdhfJ5obnonSqa
m3v8FbWhHXrhRSx4ugwAmmDaWyxmPELWqSrkrDO5ueTUXhhjiEZBTd7HjCVCSOXXhOSo3DjEVSC
lOaZBfqmXsprYyc6LJC39sroCcHYCZCv&expires_in=5183426&state=thestatepassed
Now you have both an access token and a code (that expiration applies to the token, not the code). As stated earlier, that code can be exchanged for an access token in the usual way, but the returned access token will be different from the one you just got embedded in the URI.
But, again, what you care about is in the hash fragment.
See the AngularJS doc on $location for accessing the hash. Someone more knowledgable than me can speak to how that works with routes and how best to scrape the parameters.
Well today I had the same situation and managed to resolve it in a way I could get the access token from my server.
After getting the code using response_type=code I called via GET the following url:
https://graph.facebook.com/v3.2/oauth/access_token?app_id=MY_APP_ID&code=MY_CODE&client_id=MY_APP_ID&client_secret=MY_APP_SECRET&redirect_uri=YOUR_LOGIN_REDIRECT_URI
This route returns a JSON response, which has the access_token parameter, in this way:
{
"access_token": "ABAECAEFAEFAEA...",
"type": "bearer",
"expires_in": 838383838
}
So there you go :) I hope this works for you too

Facebook server-side authentication flow: is this the right "code?"

I'm using FB.login on the JS client and want to verify the user's identity on the server. So, the client gets a signedRequest from facebook and sends it to the server. The server splits on the period, and decodes the second part of the signedRequest into a json object.
What should I be using for "code" when I send my server-side request to
https://graph.facebook.com/oauth/access_token?
client_id=YOUR_APP_ID
&redirect_uri=YOUR_REDIRECT_URI
&client_secret=YOUR_APP_SECRET
&code=CODE_GENERATED_BY_FACEBOOK
My decoded json looks something like:
{"algorithm":"HMAC-SHA256","code":"2.AQCPA_yfx4JHpufjP.3600.1335646800.1-5702286|l11asGeDQTMo3MrMx3SC0PksALj6g","issued_at":1335642445,"user_id":"5232286"}
Is that the code I need? Does it need to be B64 encoded? If this isn't the code, what code should I use?
_
What I've tried:
The request I'm trying to use is:
https://graph.facebook.com/oauth/access_token?client_id=295410083869479&redirect_uri=https://squaredme.appspot.com/facebookredirect&client_secret=44f1TOPSECRETbb8e&code=2.AQCPA_yfx4JHpufjP.3600.1335646800.1-5702286|l11asGeDQTMo3MrMx3SC0PksALj6g
but this returns the error:
{"error":{"message":"Error validating verification code.","type":"OAuthException","code":100}}
I can't tell if this is because I'm using a bad code, or what. Noteably, this is running on my local dev server, and squaredme.appspot.com definitely does NOT resolve to my IP. I don't know if facebook checks that or what - I'm assuming I'd get a better error message. Thanks for any direction!
You are trying to somehow combine the two flows together and that's why things don't work well.
When facebook POSTs into the iframe with your app url and a signed request there are two options, the easy one being that the user is already authenticated and then the signed request will have all the necessary data (including a signed request), then you just load the canvas page and use the JS SDK to get an access token there as well, but in this case there's no need to use the FB.login (since it opens a popup and will automatically close it), you can use the FB.getLoginStatus method which won't annoy the user.
If the user is not authenticated then the sign request will be missing the things you need to use the graph api.
You then redirect the user to the auth dialog, and since you are loaded in an iframe you'll need to return a html response which redirects the parent window using javascript, like:
top.location.href = "AUTH_DIALOG_URL";
When the use is done (accepted or rejected the app) he will be redirected to the "redirect_uri" you added as a parameter to the auth dialog.
If the user accepted your app then you'll be getting the "code" parameter in the query string.
You then take the code, exchange it with an access token as you posted in your question, and then redirect the user back to "apps.facebook.com/YOUR_APP".
When the page then loads the user is already authenticated and you'll be getting a full signed request.
I hope this clarifies things for you, recheck the Server-Side flow it pretty much covers it all.
I also had some trouble with that, then I found the solution here in StackOverflow.
There are two kinds of "code" provided by facebook. One comes inside the signedRequest in the cookie generated by the client-side flow. The Facebook's JS SDK handles this codes and get a access token without telling us anything.
The other type of code comes attached as a query to your redirect URI (http://www.yoururl.com/index.php?code=AAAgyiaus...), when you navigate to OAuth URL (server-side flow). With this code, you go to a Token URL and get your access token.
When you are using the server-side flow, you need to indicate a redirect URI both in the OAuth URL AND in the Token URL, and they have to be exactly the same, so a missing slash or a query string can be a lot of problem.
The codes are different from each other. When you use the both things together, appears to be impossible to get a access token using the code that was inside the cookie's signedRequest.
BUT, it is not. The magic is: the code from signedRequest is associated with NO URI, so as long as the redirect_uri is a mandatory field, all you have to do is to pass it blank when you navigate to the Token URL.
So the final solution is: grab the signedRequest from the cookie, parse it in your server to obtain the code, then read the Token URL:
https://graph.facebook.com/oauth/access_token?
client_id=YOUR_APP_ID
&redirect_uri=&client_secret=YOUR_APP_SECRET
&code=CODE_INSIDE_THE_SIGNED_REQUEST
It looks like a hack, so I don't know how long it's gonna work, but it's working right now.