How can I modify an adapter after login? - ember.js

Heres my setup:
Ember: 1.1.0-beta.1
Ember-data: 1.0.0-beta.2
# ON STARTUP
APP.ApplicationAdapter = DS.RESTAdapter.extend(
headers: {
'X-API-TOKEN': localStorage.token
}
)
This works fine if they were already logged in when they refresh...
However...
#ON LOGIN FORM SUBMIT ACTION
$.post('/sessions', data).then( (response) =>
if response.token
localStorage.token = response.token
APP.ApplicationAdapter = DS.RESTAdapter.reopen(
headers: {
'X-API-TOKEN': localStorage.token
}
)
else
#set('error_message', response.error)
The calls are still unauthorized, and the adapter keep trying to pass the old token.
So basically the 'headers' property on the adapter is not updated by the 'reopen' method. Whats the proper way to change the adapter in ember-data?
Thanks guys

Once instantiated you should get your adapter from the container.
But since it's not very clear from where you are making the request, try something like this:
#ON LOGIN FORM SUBMIT ACTION
$.post('/sessions', data).then( (response) =>
if response.token
localStorage.token = response.token
adapter = APP.__container__.lookup 'adapter:Rest'
adapter.set('headers', { 'X-API-TOKEN': localStorage.token })
else
#set('error_message', response.error)
Note: using App.__container__ is not recomended for production code depending from where you are making the request it would be more appropriate to get the container in a more clean way.
Update in response to your last comment
In the case you need access to the container in a controller then:
APP.LoginController = Ember.ObjectController.extend
...
adapter = #get('container').lookup 'adapter:application'
adapter.set('headers', { 'X-API-TOKEN': localStorage.token })
...
should get you the adapter.
Hope it helps.

Headers can be used as a computed property to support dynamic headers.
You can use the volatile function to set the property into a non-cached mode causing the headers to be recomputed with every request.
APP.ApplicationAdapter = DS.RESTAdapter.extend(
headers: function() {
return {
'X-API-TOKEN': localStorage.token
};
}.property().volatile()
)
URL's:
toc_headers-customization
method_volatile

Related

How would I update the authorization header from a cookie on a graphQL apollo mutation or query

I have the following _app.js for my NextJS app.
I want to change the authorization header on login via a cookie that will be set, I think I can handle the cookie and login functionaility, but I am stuck on how to get the cookie into the ApolloClient headers autorization. Is there a way to pass in a mutation, the headers with a token from the cookie. Any thoughts here???
I have the cookie working, so I have a logged in token, but I need to change the apolloclient Token to the new one via the cookie, in the _app.js. Not sure how this is done.
import "../styles/globals.css";
import { ApolloClient, ApolloProvider, InMemoryCache } from "#apollo/client";
const client = new ApolloClient({
uri: "https://graphql.fauna.com/graphql",
cache: new InMemoryCache(),
headers: {
authorization: `Bearer ${process.env.NEXT_PUBLIC_FAUNA_SECRET}`,
},
});
console.log(client.link.options.headers);
function MyApp({ Component, pageProps }) {
return (
<ApolloProvider client={client}>
<Component {...pageProps} />
</ApolloProvider>
);
}
export default MyApp;
UPDATE:I've read something about setting this to pass the cookie int he apollo docs, but I don't quite understand it.
const link = createHttpLink({
uri: '/graphql',
credentials: 'same-origin'
});
const client = new ApolloClient({
cache: new InMemoryCache(),
link,
});
UPDATE: So I have made good progress with the above, it allows me to pass via the context in useQuery, like below. Now the only problem is the cookieData loads before the use query or something, because if I pass in a api key it works but the fetched cookie gives me invalid db secret and its the same key.
const { data: cookieData, error: cookieError } = useSWR(
"/api/cookie",
fetcher
);
console.log(cookieData);
// const { loading, error, data } = useQuery(FORMS);
const { loading, error, data } = useQuery(FORMS, {
context: {
headers: {
authorization: "Bearer " + cookieData,
},
},
});
Any ideas on this problem would be great.
If you need to run some GraphQL queries after some other data is loaded, then I recommend putting the latter queries in a separate React component with the secret as a prop and only loading it once the former data is available. Or you can use lazy queries.
separate component
const Form = ({ cookieData }) => {
useQuery(FORMS, {
context: {
headers: {
authorization: "Bearer " + cookieData,
},
},
});
return /* ... whatever ... */
}
const FormWrapper = () => {
const { data: cookieData, error: cookieError } = useSWR(
"/api/cookie",
fetcher
);
return cookieData ? <Form cookieData={ cookieData }/> : ...loading
}
I might be missing some nuances with when/how React will mount and unmount the inner component, so I suppose you should be careful with that.
Manual Execution with useLazyQuery
https://www.apollographql.com/docs/react/data/queries/#manual-execution-with-uselazyquery

CSRF_Token in POST method using Vue.js and Django Rest Framework

I'm trying to send a POST request from a Vue.js template to my API created with Django.
When sending I get a 403 CSRF token missing or incorrect error. Since I separated the front and the back, I don't have a view with {csrf_token} on the Django side.
How do I send my form?
I tried some exemples on the web using cookies but i'm beginners and need more explaination about the POST subject and CSRF
I have a Djano View (and urls associated) juste like this :
def get_csrf_token(request):
token = get_token(request)
return JsonResponse({'token': token})
Whe i'm requesting the url, obtained the JSON with the token.
And on the Front side i'm using this method to get the Token :
getToken: function() {
this.loading = true;
this.$http.get('/qualite/get-token/')
.then((response) => {
this.token =response.data;
this.loading = false;
})
.catch((err) => {
this.loading = false;
console.log(err);
})
},
addNc: function() {
let headers = {
'Content-Type': 'application/json;charset=utf-8'
};
if(this.token !== '') {
headers['HTTP_X-XSRF-TOKEN'] = this.token
}
this.loading = true;
this.$http.post('/qualite/api/nc/',this.newNc, {headers: headers})
.then((response) => {
this.loading = false;
})
.catch((err) => {
this.loading = false;
console.log(err)
})
},
For the CSRF you get by default after user login aside with the session, if you're using SessionAuthentication (It's the default authentication used in DRF).
You have to send it with each request in the header, you can refer the this link to know more about the header sent, as it's name is changed and can be configured.
Note also that in the settings you have to make sure that CSRF_COOKIE_HTTPONLY is set to False (which is the default), to be able to read it from the client side JS.
Another path would be removing CSRF enforcement per requests (But it's highly not recommended for security concerns), you can find more about this in the answer here.
Use a Token-based authentification.
Same issue i was encountered with,
the problem was, i had used Class based view and at the time of registered the url i forget to mention as_view() with class Name.
ex:- class PostData(APIView)
before :- path('post_data', PostData)
after correction:- path('post_data', PostData.as_view())

Fetch API for Django POST requests

I'm trying to remove jQuery from a React/Redux/Django webapp and replace the $.ajax method with the Fetch API. I've more or less got all my GET requests working fine and I seem to be able to hit my POST requests, but I cannot seem to format my request in such a way as to actually get my POST data into the Django request.POST object. Every time I hit my /sign_in view, the request.POST object is empty. My entire app's backend is built around using Django forms (no Django templates, just React controlled components) and I would really like to not have to rewrite all my views to use request.body or request.data.
Here is all the code I can think that would be relevant, please let me know if there's more that would be helpful:
This is the curried function I use to build my full POST data and attach the CSRF token:
const setUpCsrfToken = () => {
const csrftoken = Cookies.get('csrftoken')
return function post (url, options) {
const defaults = {
'method': 'POST',
'credentials': 'include',
'headers': {
'X-CSRFToken': csrftoken,
'Content-Type': 'application/x-www-form-urlencoded'
}
}
const merged = merge(options, defaults)
return fetch(url, merged)
}
}
export const post = setUpCsrfToken()
This is the API method I use from my React app:
export const signIn = data => {
return post('/api/account/sign_in/', data)
}
The data when it is originally packaged up in the React app itself is as simple as an object with string values:
{
email: 'email#email.com',
password: 'password
}
I've looked at these questions and found them to be nominally helpful, but I can't figure out to synthesize an answer for myself that takes into account what I assume is some of the intricacies of Django:
POST Request with Fetch API?
Change a jquery ajax POST request into a fetch api POST
Convert JavaScript object into URI-encoded string
Is there a better way to convert a JSON packet into a query string?
Thanks!
You have to set the appropriate X-Requested-With header. jQuery does this under the hood.
X-Requested-With: XMLHttpRequest
So, in your example, you would want something like:
const setUpCsrfToken = () => {
const csrftoken = Cookies.get('csrftoken')
return function post (url, options) {
const defaults = {
'method': 'POST',
'credentials': 'include',
'headers': new Headers({
'X-CSRFToken': csrftoken,
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'X-Requested-With': 'XMLHttpRequest'
})
}
const merged = merge(options, defaults)
return fetch(url, merged)
}
}

Ember Simple Auth immediately invalidates the session after authenticating with Torii

I am trying to set up Torii with my own OAuth flow and Ember-Simple-Auth. I can get a successful authentication event, but immediately after I authenticate, the invalidateSession trigger is fired causing my session to end. I can see this by intercepting sessionInvalidated() in /app/routes/application.js (which has the ApplicationRouteMixin).
Have any of you come across this? Is there something peculiar that would cause an immediate session validation? Any advice would be greatly appreciated.
EDIT: I think it has to do with the torii popup code because the first return works, but the second doesn't. Any thoughts?
import OAuth2 from 'torii/providers/oauth2-code';
import {configurable} from 'torii/configuration';
export default OAuth2.extend({
name: 'api',
init() { this.set('clientID', this.get('apiKey')); },
baseUrl: configurable('baseUrl'),
redirectUri: configurable('redirectUri'),
responseParams: ['access_token', 'user_id', 'first_name'],
requiredUrlParams: ['client_id', 'redirect_uri', 'response_type'],
open() {
let name = this.get('name');
let url = this.buildUrl();
let redirectUri = this.get('redirectUri');
let responseParams = this.get('responseParams');
// this return works
return { 'yes' : 'no' }
// this return causes the immediate invalidation
return this.get('popup').open(url, responseParams).then((authData) => {
var missingResponseParams = [];
responseParams.forEach(function(param){
if (authData[param] === undefined) {
missingResponseParams.push(param);
}
});
if (missingResponseParams.length){
throw new Error("The response from the provider is missing " +
"these required response params: " + missingResponseParams.join(', '));
}
return {
access_token: authData.access_token,
first_name: authData.first_name,
user_id: authData.user_id,
provider: name,
redirectUri: redirectUri
};
});
}
});
the real answer is using this fork: https://github.com/simplabs/ember-simple-auth/pull/931 (hopefully it'll be in master soon).
You might have this.get('session').invalidate(); somewhere. Probably in one of your controllers action properties. You would usually put that in your actions for your logout button. Maybe you copy and pasted it by accident. If you post some code I might be able to look at it some more

Basic HTTP Authentication in Ember Data REST Adapter

Is there a way in Ember.js (and Ember-data) to send credentials to an api that requires Basic HTTP Authentication? I can see how it's done in JQuery here, but don't see any straightforward way to do it in Ember. I thought maybe adding something to the header would work (see below in coffeescript), but no success:
App.AuthAdapter = DS.RESTAdapter.extend(
host: 'https://my-api.example.com'
namespace: 'v1'
headers:
"Authorization Basic fooUsername:barPassword"
...
You can extend the default Rest adapter and add a headers hash which will be included in the ajax that's sent.
App.ApplicationAdapter = DS.RESTAdapter.extend(
headers:
withCredentials: true
Authorization: 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='
)
Or you could take it a step farther and override the ajax method
App.ApplicationAdapter = DS.RESTAdapter.extend(
ajax: (url, type, hash) ->
adapter = this
new Ember.RSVP.Promise((resolve, reject) ->
hash = hash or {}
hash.url = url
hash.type = type
hash.dataType = "json"
hash.context = adapter
if hash.data and type isnt "GET"
hash.contentType = "application/json; charset=utf-8"
hash.data = JSON.stringify(hash.data)
if adapter.headers isnt `undefined`
headers = adapter.headers
hash.beforeSend = (xhr) ->
forEach.call Ember.keys(headers), (key) ->
xhr.setRequestHeader key, headers[key]
hash.success = (json) ->
Ember.run null, resolve, json
hash.error = (jqXHR, textStatus, errorThrown) ->
Ember.run null, reject, adapter.ajaxError(jqXHR)
Ember.$.ajax hash
)
)
Can you use $.ajaxPrefilter? e.g.
Ember.$.ajaxPrefilter (options) ->
options.xhrFields = { withCredentials: true }
options.username = 'fooUsername'
options.password = 'barPassword'
true # need to return non-falsy here
As #gerry3 stated $.ajaxPrefilter is a valid solution.
But if you want to solve a problem of dynamically changing your Headers AFTER an event, for instance, a successful LOGIN attempt, then you need to put more wires. In my case I need to send back a 'Token' Header that is provided by the server after a successful AJAX-login. But, of course, when the user initiates the App he's not logged-in already.
The problem is that once you reopen or extend the RESTAdapter, or define an ajaxPrefilter, even if you're binding it to a value (or localStorage as in my case) the class won't be following the current variable value. It's like a snapshot taken at some moment. So it's useless in my scenario.
I'm following Embercast Client Authentication which is a good start (code available), but instead of jQuery data-fetching I'm using Ember-Data.
So the trick is to observe the token and re-define the ajaxPrefilter as many times as you need it.
tokenChanged: function() {
this.get('token')=='' ?
localStorage.removeItem('token') :
localStorage.token = this.get('token');
$.ajaxPrefilter(function(options, originalOptions, xhr) {
return xhr.setRequestHeader('Token', localStorage.token);
});
}.observes('token')
Therefore, when the user logs-in he'll have a valid token and send it in every request to the server via the RESTAdapter.
Hope this helps someone.