Ember-simple-auth and ember-fetch no authorization in fetch query - authentication

I am using ember with ember-simple-auth(1.7.0) for authentication. Here is the application adapter function :
authorize(xhr) {
let { email, token } = this.get('session.data.authenticated');
let authData = `Token token="${token}", email="${email}"`;
xhr.setRequestHeader('Authorization', authData);
}
When I use ember-fetch(5.1.3) there is no header for authentication :
fetch('/appname/v1/user/count'+count_params).then((response)=>{return response.json()})
The same model do successful emberDS query with the auth info in the header.
How can I add the information to the fetch headers ?
EDIT :
This is the service I created to wrap fetch :
import Service from '#ember/service';
import fetch from 'fetch';
import { inject as service} from "#ember/service"
export default Service.extend({
fetch(url){
let { email, token } = this.get('session.data.authenticated')
let authData = `Token token="${token}", email="${email}"`
return fetch(url,{headers: {'Authorization': authData}}).then(
(response)=>{return response.json()}
)
},
session: service()
});

You need to create fetch wrapper service and use it, instead of "raw" fetch and boilerplating.
Raw usage possible with headers - https://github.com/github/fetch#post-json

Related

Ember JSON API Adapter - customise request URL using dynamic param

When the action below is called in a route, the default the Ember JSON API Adapter will send a PATCH request to ${HOST}/${NAMESPACE}/${MODEL}/${ID}.
saveChanges: function(record) {
return record.save();
},
I would like to be able to send a single PATCH request to ${HOST}/${NAMESPACE}/${MODEL}/************/${ID} where the value of ************ can be passed to the action as a dynamic parameter when calling record.save().
Is there any way to do this using the JSONAPI adapter, or do I have to just use a vanilla AJAX request?
You could customize your application- or model-adapter
ember generate adapter application
or
ember generate adapter model-name
app/adapters/application.js or app/adapters/model-name.js
import DS from 'ember-data';
export default DS.JSONAPIAdapter.extend({
namespace: 'api',
urlForUpdateRecord(id, modelName, snapshot) {
let originalUpdateURL = this._super(...arguments);
let { adapterOptions } = snapshot;
if (adapterOptions && adapterOptions.customString) {
let modelPath = this.pathForType(modelName);
return originalUpdateURL.replace(`/${modelPath}/`, `/${modelPath}/${adapterOptions.customString}/`);
}
return originalUpdateURL;
}
});
after that you can call save-method of your model with adapterOptions passed:
this.get('model').save({
adapterOptions: { customString: 'hello-world' }
});
after that your patchURL will look like:
/api/your-model/hello-world/1

How to Create Aurelia Services without Redundant Header Configuration?

I'm currently working on a project using Aurelia as the front-end framework, and I'm wondering if there's a more eloquent and less redundant way to set the request header in my API services. The following is an example.
In this Post service, I have created a configureHeaders method that I'm calling prior to every API call because, otherwise, I run into the case where the web token has changed but the request header isn't updated. While creating this configureHeaders method is a functional workaround, I have to do it for each of my services, and it's feeling very redundant.
Is there a way to configure the request header application-wide so that I don't have to create a configureHeaders method for each service and call it for each request?
import {inject} from 'aurelia-framework';
import {HttpClient} from 'aurelia-http-client';
import environment from 'environment';
#inject(HttpClient)
export class Post {
constructor(http) {
this.http = http;
}
configureHeaders() {
this.token = window.localStorage.getItem('token') || null;
this.http = this.http
.configure(x => {
x.withBaseUrl(environment.serverBaseURL);
x.withHeader('Authorization', `Bearer ${this.token}`);
});
}
getPosts() {
this.configureHeaders();
return this.http.get('post')
.then(posts => {
return JSON.parse(posts.response);
});
}
}
As R.Richards commented, Aurelia's HttpClient Interceptor is what you're after.
Here's a class example - as opposed to object with anonymous functions
1.) Declare the interceptor
import {Interceptor, HttpResponseMessage, RequestMessage} from 'aurelia-http-client'
export class CustomInterceptor implements Interceptor {
request(request: RequestMessage): RequestMessage {
//Do request interceptor here
return request;
}
response(response: HttpResponseMessage): HttpResponseMessage{
//Do response interception here
return response;
}
}
2.) Register the interceptor as part of your default http client within your main.js
import {CustomInterceptor} from 'path/to/custom-interceptor'
...
...
http.configure(config => {
//config stuff here
).withInterceptor(new CustomInterceptor())
This should suit your eloquence!

react-native-fbsdk post message facebook Graph API FBGraphRequest

I went through FBSDK Sharing documentation here, but I can't find a simple example where one can just post a simple message to timeline (not a link, not a photo, not a video) using FBShareDialog.
I know I can do it running a web request which essentially does this:
POST graph.facebook.com/me/feed?
message="My new post message!"&
access_token={your-access-token}
as described in Graph API docs, but again - I want to use ShareDialog to have consistent UI.
How do I do it? Thank you.
Note: All user lower case "post" refers to the act of posting to a users wall. All upper case "POST" refers to HTTP request method.
Facebooks offical react native SDK is located here https://developers.facebook.com/docs/react-native/
Note there are three different component:
Login
Sharing
Graph API.
The first two are self explanatory and the examples are provided on the site.
The Graph API is the primary way to get data in and out of Facebook's
social graph. It's a low-level HTTP-based API that is used to query
data, post new stories, upload photos and a variety of other tasks
that an app might need to do.
Facebook Graph API is just a REST API which lets you interact with the fb data via HTTP methods( GET, POST, DELETE etc). react-native-fbsdk just layer on top of it which makes it easier to make these request.
There are two prerequisites to posting to a user time.
Ensuring your fb app is correctly setup: https://developers.facebook.com/apps/
Obtaining a user access token with publish_actions permission can be used to publish new posts.
Once you have obtained these you can post a message using the react native GRAPH API.
But first lets have a look at how you would do this simply using HTTP rather then the RN-SDK:
https://developers.facebook.com/docs/graph-api/reference/v2.7/user/feed
POST /v2.7/me/feed HTTP/1.1
Host: graph.facebook.com
message=This+is+a+test+message
According to this we need to make a POST request to the location /v2.7/me/feed with the message defined as a paramater.
To finally answer your question how to we post to the users timeline using the react native sdk (). Lets have a look at the rnsdk docs: https://developers.facebook.com/docs/react-native/graph-api
It seems like we need two objects GraphRequest (to create a request) and GraphRequestManager (to send the request)
const FBSDK = require('react-native-fbsdk');
const {
FBGraphRequest,
FBGraphRequestManager,
} = FBSDK;
Since there is no example provided on how to post to the user wall using these two objects we need to look into the source code:
https://github.com/facebook/react-native-fbsdk/blob/master/js/FBGraphRequest.js
We can see from the constructor it takes three parameters:
/**
* Constructs a new Graph API request.
*/
constructor(
graphPath: string,
config: ?GraphRequestConfig,
callback: ?GraphRequestCallback,
)
We know the graphPath = "/me/feed" from the Graph API docs. The callback will just be a function called upon return of the request. This leaves us with the config object, which is defined in the source as:
type GraphRequestConfig = {
/**
* The httpMethod to use for the request, for example "GET" or "POST".
*/
httpMethod?: string,
/**
* The Graph API version to use (e.g., "v2.0")
*/
version?: string,
/**
* The request parameters.
*/
parameters?: GraphRequestParameters,
/**
* The access token used by the request.
*/
accessToken?: string
};
So our config object will look something like this:
const postRequestParams = {
fields: {
message: 'Hello World!'
}
}
const postRequestConfig = {
httpMethod: 'POST',
version: 'v2.7',
parameters: postRequestParams,
accessToken: token.toString() //pre-obtained access token
}
Putting it altogether:
const FBSDK = require('react-native-fbsdk');
const {
FBGraphRequest,
FBGraphRequestManager,
} = FBSDK;
_responseInfoCallback(error: ?Object, result: ?Object) {
if (error) {
alert('Error fetching data: ' + error.toString());
} else {
alert('Success fetching data: ' + result.toString());
}
}
const postRequestParams = {
fields: {
message: 'Hello World!'
}
}
const postRequestConfig = {
httpMethod: 'POST',
version: 'v2.7',
parameters: postRequestParams,
accessToken: token.toString()
}
const infoRequest = new GraphRequest(
'/me/feed',
postRequestConfig,
this._responseInfoCallback,
);
new FBGraphRequestManager().addRequest(infoRequest).start();
I posted to facebook with react native 0.43 by above code but i changed on postRequestParams
const postRequestParams = {
message: {
string: 'Hello World!'
}
}
Here is all of me.
const FBSDK = require('react-native-fbsdk');
const {
GraphRequest,
GraphRequestManager,
AccessToken
} = FBSDK;
class PostScreen extends React.Component {
postToFacebook = () => {
AccessToken.getCurrentAccessToken().then(
(data) => {
let tempAccesstoken = data.accessToken;
const _responseInfoCallback = (error, result) => {
console.log(result);
}
const postRequestParams = {
message: {
string: "Hello world!"
}
}
const postRequestConfig = {
httpMethod: "POST",
version: "v2.9",
parameters: postRequestParams,
accessToken: tempAccesstoken
}
console.log(postRequestConfig);
const infoRequest = new GraphRequest(
"/me/feed",
postRequestConfig,
_responseInfoCallback,
);
console.log("infoRequest");
console.log(infoRequest);
new GraphRequestManager().addRequest(infoRequest).start();
});
}
}

Ember Simple Auth - Custom Authorizer Breaks Authentication

I'm using the ember-cli-simple-auth addon with Torii for my authentication flow.
So far I've managed to get authentication working using both a custom Torii Provider combined with a custom Simple Auth Authenticator.
I'd now like to use a custom Simple Auth Authorizer to inject the access token into requests.
Following the documentation https://github.com/simplabs/ember-simple-auth#implementing-a-custom-authorizer I've added a custom authorizer & initializer
authorizers/myservice.js
import Base from 'simple-auth/authorizers/base';
import Ember from 'ember';
export default Base.extend({
/**
#method authorize
#param {jqXHR} jqXHR The XHR request to authorize (see http://api.jquery.com/jQuery.ajax/#jqXHR)
#param {Object} requestOptions The options as provided to the `$.ajax` method (see http://api.jquery.com/jQuery.ajaxPrefilter/)
*/
authorize: function(jqXHR) {
var accessToken = this.get('session.content.token');
if (this.get('session.isAuthenticated') && !Ember.isEmpty(accessToken)) {
jqXHR.setRequestHeader('Authorization', 'Bearer ' + accessToken);
}
}
});
initializers/authorization.js
import MyserviceAuthorizer from '../authorizers/myservice';
export function initialize(container, application) {
container.register('authorizer:myservice', MyserviceAuthorizer);
}
export default {
name: 'authorization',
before: 'simple-auth',
initialize: initialize
};
& included in config/environment.jsin the development environment
ENV['simple-auth'] = {
authorizer: 'authorizer:myservice',
crossOriginWhitelist: ['*']
}
Unfortunately by adding this it has now broken the authentication.
It looks like Torii is no longer receiving the response.
The response from the provider is missing these required response params: access_token, token_type, expires_in
I've included both the Torii Provider code & Simple Auth Authenticator code here too.
Any suggestions or help would be very much appreciated, i'm a bit stuck with this.
torii-providers/myservice.js
import Provider from 'torii/providers/oauth2-bearer';
import {configurable} from 'torii/configuration';
import env from '../config/environment';
export default Provider.extend({
name: 'myservice',
baseUrl: (env.api_host + '/oauth/authorize'),
responseParams: ['access_token', 'token_type', 'expires_in'],
redirectUri: configurable('redirectUri', function(){
// A hack that allows redirectUri to be configurable
// but default to the superclass
return this._super();
})
});
And a custom Simple Auth authenticator
authenticators/myservice.js
import Ember from 'ember';
import Base from 'simple-auth/authenticators/base';
import ajax from 'ic-ajax';
export default Base.extend({
restore: function(data) {
return new Ember.RSVP.Promise(function(resolve, reject) {
if(!Ember.isEmpty(data.currentUser)) {
resolve(data);
} else {
reject();
}
});
},
authenticate: function(options) {
return this.fetchOauthData(options).then(this.fetchUserData.bind(this));
},
fetchUserData: function(oauthData) {
var token = oauthData.token.access_token;
return ajax({
url: '/api/v1/users/me',
type: "GET",
beforeSend: function (xhr) {
xhr.setRequestHeader("Authorization", "Bearer " + token);
}
}).then(function(userJSON){
return {
currentUser: userJSON.user,
token: token
};
});
},
fetchOauthData: function(options) {
return new Ember.RSVP.Promise(function(resolve, reject) {
options.torii.open(options.provider).then(function(oauthData) {
resolve({
provider: oauthData.provider,
token: oauthData.authorizationToken
});
}, function(error) {
reject(error);
});
});
}
});
This might be related to the fact, that Ember CLI automatically registers everything under app folder in the container. Although the following quote from Ember CLI documentation doesn't explain that clearly, but it gives a hint:
All modules in the app folder can be loaded by the resolver but typically classes such as mixins and utils should be loaded manually with an import statement.
If your authorizer file is app/authorizers/myservice.js, Ember CLI will register it under 'authorizer:myservice' name on a container. Container in turn will create a singleton instance when it is looked up. Since you do the same registration in the initializer, there may be some kind of a conflict.

Facebook login with Torii and Simple-Auth : no authentication data returned

I tried to setup a facebook login using Torii and Simple-Auth Torii authenticator. I am using ember-cli.
Here is my configuration :
// config/environment.js
ENV['torii'] = {
providers: {
'facebook-oauth2': {
apiKey: 'my facebook app id'
}
}
}
Here is my login code :
// app/controllers/login.js
import Ember from 'ember';
import LoginControllerMixin from 'simple-auth/mixins/login-controller-mixin';
export default Ember.Controller.extend(LoginControllerMixin, {
actions: {
authenticateWithTorii: function(){
this.get('session').authenticate(
'simple-auth-authenticator:torii',
'facebook-oauth2'
).then(
function(data) {
alert('SUCCESS ' + data);
},
function(error) {
alert('There was an error when trying to sign you in: ' + error);
}
);
}
}
});
The popup is opening asking for authorization, then I see the alert with "SUCCESS UNDEFINED". I thought I will get some data I could use on my backend to definitely authentify the user (access token or facebook uid, etc...).
Did I miss something in the way to use Torii with Simple-Auth ?
The promise returned by Session#authenticatedoes not have a value but everything that the torii authenticator resolves with is available via the session as soon as that's authenticated (which it is in your case as the returned promise has resolved). You could access e.g. a token property with
var _this = this;
this.get('session').authenticate(
'simple-auth-authenticator:torii',
'facebook-oauth2'
).then(function(data) {
alert('SUCCESS ' + _this.get('session.token'));
})
You can also inspect the session's content property (which you shouldn't use otherwise as it's private but for debugging that's ok) to find out what's available:
console.log(this.get('session.content'))