In my Rails app, I am trying to set a cookie to be picked up by Ember Simple Auth's cookie store after the page has loaded. I am using the Ember Simple Auth OAuth2 authorizer.
Right now, I am just planting the OAuth data directly as the cookie value:
{
"token_type": "bearer",
"access_token": "3ec78864cc017982fdeeb0c092bfbea3f104df1e18c9c67f222581d9353f3fce",
"refresh_token": "cb03c07b8845ea7b40251b0df46839177bd7b51b3dd1d23f167890b9e1549f07",
"created_at": 1436454055,
"expires_in": 7060,
"expires_at": 1436461254
}
I'm guessing this isn't what Ember Simple Auth expects because the syncData function reads it once and then replaces it with this value after the next cookie poll:
{ secure: {} }
What should the data look like for OAuth 2? I'm guessing it's the same no matter how it's stored (cookie vs. local storage vs. ephemeral storage).
After looking at this screenshot from this post, I figure I'm probably way off, and I've been having trouble understanding where to poke around in the Ember Simple Auth source to figure this out.
Ember Simple Auth only uses the cookie to store its internal state. The cookie cannot be set from the server and also should not be used on the server side. The library is solely meant for implementing token authentication for stateless (= cookie-less) APIs.
See the README for more info about how OAuth 2.0 works with ESA: https://github.com/simplabs/ember-simple-auth/tree/master/packages/ember-simple-auth-oauth2#ember-simple-auth-oauth-20
I believe that Marco's advice in the accepted answer should be followed if at all possible.
But, poking around a little more, I figured out that the cookie content would need to look like this in order for Ember Simple Auth OAuth 2 to recognize it:
{
"secure": {
"authenticator": "simple-auth-authenticator:oauth2-password-grant",
"token_type": "bearer",
"access_token": "3ec78864cc017982fdeeb0c092bfbea3f104df1e18c9c67f222581d9353f3fce",
"refresh_token": "cb03c07b8845ea7b40251b0df46839177bd7b51b3dd1d23f167890b9e1549f07",
"created_at": 1436454055,
"expires_in": 7060,
"expires_at": 1436461254
}
}
Of course, there are some drawbacks to this approach, namely that upgrading Ember Simple Auth could break if it changes the format of how it stores this data.
If you set cookies from another app like I'm attempting to do, you'd need to be mindful about reviewing this format after each update of Ember Simple Auth. The best way to accomplish this is to create a blank Ember app with Simple Auth installed and configured, then review the format of the data that it stores after you sign in to the app.
Related
I would like to test Google Indexing API with Postman.
I have already configured the api correctly on https://console.cloud.google.com/ for using it on a wordpress site with the plugin Instant indexing and everything works ok.
Now I simply want to do a POST call with Postman, in particular I did not found anything clear about the type of authentication (Oauth 2) to use for the call.
{
"error": {
"code": 401,
"message": "Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
"status": "UNAUTHENTICATED",
"details": [
{
"#type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "CREDENTIALS_MISSING",
"domain": "googleapis.com",
"metadata": {
"method": "google.indexing.v3.UrlService.PublishUrlNotification",
"service": "indexing.googleapis.com"
}
}
]
}
}
Anyone can provide me an example? Thank you.
As stated in the comments, Google API's normally use OAuth2. Some exceptions might use an API key, but even then most of those still limit the data you can access with an API key versus what you can access using OAuth2
This particular API does indeed use OAuth2 as per the docs. This means you need an access token (as the error message states), so you will need to generate one.
Easiest would be to use a library for one of the many programming languages, as per the docs here.
I'll give a simple example in python as that is my preferred language. The following code uses the google-auth library (docs). It's the base library to deal with credential objects. You will need to download the JSON representation for the service account you created. See the relevant docs. Note that you should handle this file as if it were a password, same as with the access token itself, although the access token has a default lifetime of 3600 seconds, AKA 1h.
If you get an error message that the token is expired, create a new one. Read up on refresh tokens for that, but that's beyond the scope of this question.
Code was tested on a virtualenv for python 3.10
requirements.txt
google-auth
requests
main.py
from google.oauth2 import service_account
import google.auth.transport.requests
# create credentials object
creds= service_account.Credentials.from_service_account_file("/path/to/serviceaccount.json")
# we need a scoped credentials object as per
# https://developers.google.com/search/apis/indexing-api/v3/prereqs#requirements
scoped_creds = creds.with_scopes(['https://www.googleapis.com/auth/indexing'])
# create a request object for the refresh method
request = google.auth.transport.requests.Request()
# make the library do it's magic and get a token
scoped_creds.refresh(request)
print(scoped_creds.token)
This will print out an access token. (if it prints with dots (....) at the end, remove them.
Another easy option would be to (ab)use the gcloud command line tool. There's a couple of steps to this to get to work. You can use the cloud shell as an easy way to do this.
Download or copy the serviceaccount JSON file I mentioned above.
Activate the serviceaccount in gcloud using:
gcloud auth activate-service-account SERVICE_ACCOUNT#DOMAIN.COM --key-file=/path/key.json
Print the access token with this command:
gcloud auth print-access-token
The hard and masochistic way would be to use something like CURL or HTTPS requests manually to generate the token. Feel free to do so, but I'm just going to point you to the docs for that. It's a bit of a pain in the posterior.
You can test the token as explained in this answer.
Now that you have the access token, you can use it in POSTMAN by setting it in the header for the call. See this nice answer, but basically add the following request header key/value pair, replacing TOKEN with the generated token.
KEY: Authorization
VALUE: Bearer TOKEN
For anyone interested I managed to make it work by following these steps:
To obtain the authentication token with Postman first go to https://console.cloud.google.com/apis/credentials and in your web application ID client set https://oauth.pstmn.io/v1/callback as Authorized redirect URIs
Now in Postman Application in Authorization tab select OAuth 2.0 and configure the fields according to your client_secret.json (image)
Now click on 'Get new access Token' and you should get it
Specify the url and type as raw json body (image)
I'm wondering, why use Login when you have an Authentication Token?
I mean I'm using Postman to test my urls, serializers and views.
I can log in and I have to use a different urls to get my access token and my refresh token.
Login: POSThttp://localhost:8000/login/
{
"username":"Max",
"password":"Yolo1234"
}
-----
{
"id": 2,
"username": "Max",
"is_a": true,
"is_e": false
}
Token: POSThttp://localhost:8000/api/token/
{
"username":"Max",
"password":"Yolo1234"
}
-----
{
"refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTU4Nzc1OTQ5NCwianRpIjoiOWUzNjg1OTZhZWViNDRiNWE2Nzg3Y2E0ZDhkODQ5OWQiLCJ1c2VyX2lkIjoyfQ.W9a2fCxUF9Hrf51l-Ecx7nt2tmt2QvLhr4pp2DBUuvE",
"access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTg3NjgwMjk0LCJqdGkiOiIwNDI3MmJhMjljNmM0ODk3ODg4YTI5YTBkZjViZDljZCIsInVzZXJfaWQiOjJ9.0sid4rSbDUX3LHX7X74Cw1H9_lSlwD5ppfC3ctLGFSo"
}
And when I'm trying to POST an article, I need to authenticate with my access token.
But when I log out, I can still POST an article by using my access token without being logged in.
So I have some difficulty understanding the need to have the two in the project.
Thanks for your answers!
Tokens are usually the preferred way for API to authenticate since it is described in RFC such as RFC 7235.
However browsers are not able to deal with tokens - they are not very... convenient to humans.
Instead, sites have been using cookies to store session identifiers and websites can flag a session as being authenticated or not. The authentication is possible through login/logout pages where the website can ensure the username / password matches and then flag the session with that information. Hopefully, it'll not be stored client side for obvious security reasons. DRF uses SessionAuthentication for that.
It is quite handy when the user logs in on a website and can use that session on the API without requiring extra action from the user.
Note that you may use session authentication with other things than browsers but that's usually not what is done.
I’m currently porting an addon from the jetpack to the WebExtension API. I need to continuously update a browser action (toolbar button) with data (e.g. set its badge text).
For this, I would like to do a request from a background script in my extension to an API of the page, which is accessible when the user is logged in (i.e. a cookie is set). What I did so far:
I gave myself host permissions, which is mentioned to be necessary for request from content scripts.
However, content scripts are for injecting JS into pages the user visits.
I created a background script that uses fetch to do a request to the API.
However, when queried from the background script, the API tells me that nobody is logged in, while I can access it with the browser flawlessly.
This is the relevant part of the manifest.json:
{
"background": {
"scripts": ["background.js"]
},
"permissions": [
"*://subdomain.domain.com/*"
]
}
How can I have a continuously running background script that can use the user’s cookie to access this API?
Firefox has stricter cors restrictions for cookies. I solved this by doing the api call from a content-script injected on the page with the same domain. That api call generated an auth token that was used for api calls from the background page, and enabling cors on backend for requests containing token in auth header.
You'll need to supply the credentials option in the arguments to fetch()
https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch
I have an ember app that connects with github, but the authentication flow returns the authorization code, not the access token, and I don't really know how to do the exchange...well, at least I didn't found any examples on the internet.
I'm kinda new to ember, this is what I got so far
authenticator/torii.js
import Ember from 'ember';
import ToriiAuthenticator from 'ember-simple-auth/authenticators/torii';
export default ToriiAuthenticator.extend({
torii: Ember.inject.service()
});
torii-providers/github.js
import GithubOauth2Provider from 'torii/providers/github-oauth2';
export default GithubOauth2Provider.extend({
fetch(data) {
return data;
}
});
I know I may have to change something in the provider, but I don't really know where to start
I've used Torii to do GitHub auth myself. Here's my advice:
Drop ember-simple-auth and just use Torii directly. Ironically, ember-simple-auth's Torii wrapper isn't "simple".
You should go over Torii's docs to familiarize yourself with the library.
In your config/environment.js, configure Torii. Example:
torii: {
sessionServiceName: 'session',
providers: {
'github-oauth2': {
// your api key goes here
apiKey: '',
// link to your app goes here
// in development mode, it should be http://localhost:4200
redirectUri: '',
// specify OAuth scope here
scope: ''
}
}
}
Create a file called torii-adapters/application.js. Here you will need to implement the three methods .open(), .fetch(), and .close(). Note that you will receive the authorizationCode as a parameter for .open(), which you should exchange (with your auth backend) for an access token.
Oh, and you'll need an OAuth backend that keeps your client secret private. You send the authorization code from your Ember app to your OAuth backend, and the OAuth backend responds with an access token.
If none of that made any sense to you, check out this blog post, which has a good summary of OAuth. You should understand the big picture so that filling in the details is easy. :)
I'm using oAuth2.
The DataAdapterMixin automatically adds the Authorization header to all Ember Data requests. The session service authorize method can be used to add it to Ajax calls, but how can I ensure the header is added to GET requests made from the browser. I've tried creating a custom authorizer as explained in the docs, but the authorize method is not called.
The my application stores scans of invoices and statements (usually pdfs) which can only be seen by an authorized user. Based on the user's actions, I am changing the data attribute of an object tag.
This
<object data={{attachViewURL}} width="800px" height="1200px">
is rendered as something like this:
<object data="/scans/attachments/11" width="800px" height="1200px">
This works fine except the authorization.
Any advice would be most appreciated. I'm new to Ember, so if I am going about this the wrong way, please let me know.
In lieu of you answering my comment, i'm just going to put my solution here:
import Ember from 'ember';
import SimpleAuth from 'simple-auth/authorizers/base';
export default SimpleAuth.extend({
authorize: function(jqXHR) {
var token = this.get('session.secure.token');
if (this.get('session.isAuthenticated') && !Ember.isEmpty(token)) {
jqXHR.setRequestHeader('Authorization: ', 'Bearer ', token);
}
},
});
You'll likely want to do a bit more checking etc, but that's pretty much all you need to do to actually set the header; just overriding the authorize function that is built in to ember-simple-auth to set a header once the authorization has passed (As defined by both the session.isAuthenticated boolean and passed token
EDIT:
I forgot that you need to also define that you're using your local authorizer in your simple-auth config (that tripped me up for a while because I assumed that ember-simple-auth would use the overriden local authorizer by default):
ENV['simple-auth'] = {
authenticationRoute: 'login',
authorizer: 'authorizer:local',
session: 'session:user-session',
routeAfterAuthentication: 'dashboard'
}
I'll try and dig up where I got this from (because the ember-simple-auth docs aren't particularly good at describing self-rolled authorization mechanisms...)
EDIT 2:
As marcoow pointed out (and he should know, he wrote ember-simple-auth!), this is pre-1.0 behaviour. The authorizer is now part of the session service, which needs to be injected into the application. See ember-simple-labs pre-1.0 upgrade
The only approach I've managed to get working is to include the access token in the generated URL and then manually authenticate on the back end.
The URL ends up looking something like:
<object data="/scans/attachments/11?token=a0a59197-afab-438a-b6cf-11237e51a2d5" width="800px" height="1200px">
I'm not proud of this workaround. It's horrible programming, but it's the only thing I've gotten working so far.