I'm using ember-simple-auth with Cookie based authentication.
When i login the {{session.isAuthenticated}} is true, but when i reload the page this is FALSE, but the localStore didn't change.
This is my custom.js authenticator:
import Base from 'ember-simple-auth/authenticators/base';
import config from '../config/environment';
export default Base.extend({
tokenEndpoint: config.apiUrl + '/user/signIn',
restore(data) {
console.log(data);
return RSVP.resolve(data);
},
authenticate(username, password) {
return Ember.$.ajax({
url: this.tokenEndpoint,
type: 'POST',
data: JSON.stringify({
username: username,
password: password,
}),
contentType: 'application/json;charset=utf-8',
dataType: 'json',
});
},
invalidate: function () {
return Ember.RSVP.resolve();
},
});
I'm using the {{session.isAuthenticated}} in the application.hbs. So i'm injecting the session in application controller:
session: Ember.inject.service('session')
You are making a mistake. 'restore ' method is called each time we refresh our page or open another tab. Note that RSVP.resolve removes your local storage so all is unauthenticated. We use this when we want to log user out and by that it means Ember Simple Auth will remove its local storage cookie and when this cookie is not found, it always send false for isAuthenticated method.
RSVP.resolve(data);
change your restore function from this
restore(data) {
console.log(data);
return RSVP.resolve(data);
},
to this
restore(data) {
return new Promise((resolve, reject) => {
resolve(data);
});
},
Note that this 'restore' method is like a method which we have to override based on our site rules e.g if you have token authentication we will write this method as
restore(data) {
return new Promise((resolve, reject) => {
if (!Ember.isEmpty(data.token)) {
console.log(data);
resolve(data);
} else {
reject();
}
});
},
Note that how we are handling things with our own logic to see if localstorage has token attribute present in it.
Similarly we can do by getting session cookie in this method and check if it exists. If it exists then we resolve else we reject. Note that these resolve and reject are promise methods etc.
Related
I have created a Cypress command to fetch me a JWT token from my GQL API. I then set the token as a cookie to use in my tests that require authentication.
Here is my login command:
export function login(username, password) {
const { apiUrl: url } = Cypress.env();
cy.request({
method: 'POST',
url,
body: {
operationName: 'signin',
headers: {
'content-type': 'application/json'
},
query:
`mutation signin($email: String!, $password: String!) {
signin(input: {email: $email, password: $password}) {
id
token
__typename
}
}`,
variables: { email: username, password: password }
}
})
.then(response => {
const { token } = response.body.data.signin;
cookie.set('token', token, { expires: 10000000 });
cy.wait(3000);
});
}
I can see when I run the login command the cookie is set but when my test tries to visit a page within my app the cookie disappears.
describe('Entity Page', () => {
before(() => {
const { username, password } = Cypress.env();
cy.login(username, password);
cy.addEntity(newEntityId, {});
cy.wait(3000);
cy.get('#entityId').then(entityId => {
cy.visit(`/entity/${entityId}`)
cy.wait(6000)
});
});
By the time I get to addEntity the cookie disappears and I am unauthenticated. Is there something I need to do to persist cookies? I tried Cypress.Cookies.preserveOnce but this had no effect
I also tried adding the below to my support/index.js but the cookie is still removed.
Cypress.Cookies.defaults({
preserve: 'token'
})
Try it with cy.setCookie(name, value) docs.
It has a couple of defaults that might help.
domain - defaults to window.location.hostname. The domain the cookie is visible to.
expiry - defaults to 20 years into the future. Specified as seconds since 1 January 1970 (10,000,000 is only 115 days).
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
I have a Django as backend and updating the user from postman is working fine. But when I update it via React Frontend, it replies with a success message just as in Postman, but the data was not updated.
This is the update function to update:
const updateData = (e) => {
e.preventDefault();
const csrftoken = getCookie("csrf");
const cookies = new Cookies();
const url = "http://localhost:8000/usercontrol/update";
setIsLoading(true);
fetch(url, {
method: "PUT",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Authorization: "Token " + cookies.get("token"),
"X-CSRFToken": csrftoken,
},
body: JSON.stringify({
email: userinfo.email,
username: userinfo.username,
first_name: userinfo.first_name,
last_name: userinfo.last_name,
}),
}).then((response) => console.log("THE RESPONSE: ", response.json()));
setIsLoading(false);
};
This is what it prints out in the console
Since I am partially following CodingWithMitch for Django user creation with rest framework is similar to his.
Furthermore, since there is no error outputting and is working fine in Postman, I have no idea what is wrong with it.
I use an external service for authentication Stamplay ..
To authenticate with username and password, I have to make a post in ${config.host}/auth/v1/local/login
The callback for this post contain the token, so I created a custom authenticator to handle it
Custom Authenticator
export default Base.extend({
tokenEndpoint: `${config.host}/auth/v1/local/login`,
// ... Omited
authenticate(options) {
return new Ember.RSVP.Promise((resolve, reject) => {
Ember.$.ajax({
url: this.tokenEndpoint,
type: 'POST',
data: JSON.stringify({
email: options.email,
password: options.password
}),
contentType: 'application/json;charset=utf-8',
dataType: 'json'
}).then(function (response, status, xhr) {
Ember.run(function () {
resolve({
token: xhr.getResponseHeader('x-stamplay-jwt')
});
});
}, function (xhr) {
Ember.run(function () {
reject(xhr.responseJSON.error);
});
});
});
},
invalidate(data) {
return Ember.RSVP.Promise.resolve(data);
}
});
And everything works fine.. but ...
My problem
For social logins, I need to redirect the user to https://MYAPP.stamplayapp.com/auth/v1/EXTERNAL_SERVICE/connect
EXTERNAL_SERVICE can be.. github, twitter, facebook...
Then, the user is redirect to service page, and after login, the callback will be http://myapp.com/callback?jwt=XYZ
So, how can I capture the token and login the user with this token?
Tell me if I'm wrong, but I think that for Facebook you can use Torii which is working well with simple-auth. Twitter is using Oauth1.0, so it's a bit more complicated in my opinion. But Facebook / Google should be fine.
Basically, Ember will request an AuthorizationCode from Facebook API, then send it to your server. Your server will then ask Facebook API an access_token, and use it to get the user information. Finally, you can load/register your user, generate a JWT token and send it to your Ember app.
But I'm interested to know if you have found a solution for Twitter.
I want to store the user id in the session. I have this authenticate method in a custom authenticator :
authenticate: (credentials) ->
_this = this
new (Ember.RSVP.Promise) (resolve, reject) ->
Ember.$.ajax(
url: 'http://localhost:3000/api/v1/sessions'
type: 'POST'
data: session:
email: credentials.identification
password: credentials.password
).then ((response) ->
_this.set('user_id', response.user_id)
Ember.run ->
resolve token: response.token
), (xhr, status, error) ->
response = JSON.parse(xhr.responseText)
Ember.run ->
reject response.error
It's coffee script but it works like javascript. As you can this, I set 'user_id' in the session.
In app/initializers/custom-session, I have this :
import Ember from "ember";
import Session from "simple-auth/session";
export default {
name: "current-user",
before: "simple-auth",
initialize: function(container) {
Session.reopen({
setCurrentUser: function() {
return this.get('user_id')
}.observes('secure.access_token')
});
}
};
user_id always returns undefined. I found many solutions on the Internet but nothing works.
Is there a simple solution to do that?
You are setting the user_id on the authenticator (_this.set('user_id', response.user_id)) instead of the session. The user_id should be passed to the resolve method in your authenticator. That way your user id is accessible by secure.user_id in your session.