in a web app we have used 2 Auth methods
default is JWT which we are using for API’s
but for website we are using session
somehow session are not destroying on logout
i can still access session by
auth.authenticator('session').getUser()
even after this doing in logout
await auth.authenticator('session').logout()
Here is my full code for login
Route.get('/login', async ({ view ,auth,response}) => {
try{
await auth.authenticator('session').check()
response.route('/feed')
}
catch(e){
return view.render('user.login') // User login page in web
}
})
Logout route
Route.get('/logout', async ({ auth, response }) => {
await auth.authenticator('session').logout()
response.route('/login')
})
i have noticed that if i disable chrome cache from dev tools then web app works fine
I am learning about react and django. I have installed django-rest-auth to handle account creations and authentication for users. I also wanted to learn about react and I have install axios to make http request to my django rest api. I want to have a "splash" page where users would first access the site. If the user is already logged in they'll see their profile and other content. If the user isn't logged in they should be presented a login page.
Here's my App.js code I have so far.
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import logo from './logo.svg';
import './App.css';
function LoginPage(props) {
console.log('LoginPage props are:');
console.log({ props });
return (<div className="LoginPage">props are: {props}</div>)
}
function SplashPage(props) {
const [currentUser, setCurrentUser] = useState(null);
console.log('SplashPage props are:');
console.log({ props });
const userUrl = 'http://localhost:8000/rest-auth/user/';
console.log('userUrl is:' + userUrl);
axios.get(userUrl)
.then(res => { setCurrentUser(res.data); })
.catch((error) => {
console.log(error.response);
return (<div><LoginPage /></div>);
})
return (<div className="SplashPage">[{userUrl}] [{currentUser}] </div>);
}
function App() {
return (
<div>
<SplashPage />
</div>
);
}
export default App;
Heres my index.js file:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers:
serviceWorker.unregister();
When I go to http://localhost:3000 I get this result:
In the developer console looks like
I had hoped to see the content of my LoginPage function.
[UPDATED ANSWER]
You are returning <div className="SplashPage">[{userUrl}] [{currentUser}] </div> before <div><LoginPage /></div> because it is outside the axios .then() chain ( ie it called directly after the axios.get() and before any code in the .then() or the .catch() blocks )
Should work:
initialize a current user with a loaderState to avoid content flicker
Update state within the axios .then() or .catch()
Use state to determine what to return from function outside of the promises
-
function SplashPage(props) {
const [currentUser={notLoaded:true}, setCurrentUser] = useState(null);
const userUrl = 'http://localhost:8000/rest-auth/user/';
axios.get(userUrl).then(res => {
setCurrentUser(res.data);
}).catch((error) => {
console.log(error)
setCurrentUser(null)
})
//user no authorized
if(!currentUser)
return <LoginPage />
//user authorization unknown
if(currentUser.notLoaded)
return <div/>
//we have a user!
return <div className="SplashPage">{userUrl} {currentUser}</div>
}
[ORIGINAL ANSWER]
EDIT: sorry I misunderstood your question but will leave my original answer here in case someone comes looking for a related issue.
You are getting a 403 error with the message:
Authentication credentials not provided
You need to add some sort of authorization to your request (consult your django-rest-auth configuration/documentation for how it expects authorization from incoming requests).
You can either set this up for every api call manually or set this up via axios.interceptors.request.use() which you will need to import and call somewhere in your application (such as in your app.js or index.js)
The following example:
uses axios.interceptors
adds an authorization token to the Authorization header
utilizes the standard 'bearer TOKEN'
uses firebase auth to demonstrate retrieving token via async
(your actual implementation will depend on how your api is set up and your authorization flow)
addAuthHeader.js:
import axios from 'axios';
import * as firebase from 'firebase/app';
const apiUrl = 'http://localhost:8000/' // '/' if using the preferred http-proxy-middleware
export default addAuthHeader = () =>
//if firebase auth callback should be asyncasync
axios.interceptors.request.use(async (config) => {
if(config.url.startsWith(apiUrl)){
const token = await firebase.auth().currentUser.getIdToken(true)
config.headers.Authorization = `bearer ${token}`;
return config;
}
});
App.js:
addAuthHeader()
I'm using ember-simple-auth and I can see that the authentication token is returned and that my custom authenticator is putting it (code-wise) into the session.
I can use wireshark to see that the token comes back but I can't debug into the authenticator code - I can't even use debug statements as the redirect wipes the network session on google chrome.
However, it isn't in the session (google chrome tools) and the session isn't considered authenticated.
Why is the lock code correctly popping up the auth0 dialog but not saving the session? I don't really think it is something with this code but rather a config or initialization setting I'm unaware of.
I expect the session to be saved in the _setupFutureEvents as the first thing it does.
Any ideas what I can try to get it to work?
(/app/authenticators/lock.js) authenticate (options) {
return new Ember.RSVP.Promise((res) => {
this.get('lock').show(options, (err, profile, jwt, accessToken, state, refreshToken) => {
if (err) {
this.onAuthError(err);
} else {
var sessionData = { profile, jwt, accessToken, refreshToken };
// pass the NEW auth0 session data into future events
this.afterAuth(sessionData).then(response => res(this._setupFutureEvents(response)));
}
});
});
},
afterAuth (data) {
return Ember.RSVP.resolve(data);
},
_setupFutureEvents (data) {
// set the session info here
this.get('sessionData').setProperties(data);
this._clearJobs();
this._scheduleExpire();
if (this.get('hasRefreshToken')) {
this._scheduleRefresh();
}
return this.get('sessionData');
},
I have the following situation: The user enters his credentials and clicks a Login button. An API call is done in the action creator via redux-thunk. When the API call was successful, another action is dispatched containing the response from the server. After the (successful) login I want to store the users session id in a cookie (via react-cookie).
Action creator
export function initiateLoginRequest(username, password) {
return function(dispatch) {
dispatch(loginRequestStarting())
return fetch('http://path.to/api/v1/login',
{
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: username,
password: password
})
})
.then(checkStatus)
.then(parseJSON)
.then(function(data) {
dispatch(loginRequestSuccess(data))
})
.catch(function(error) {
dispatch(loginRequestError(error))
})
}
}
export function loginRequestSuccess(user) {
return {
type: ActionTypes.LOGIN_REQUEST_SUCCESS,
user
}
}
Reducer
export default function user(state = initialState, action) {
switch (action.type) {
case ActionTypes.LOGIN_REQUEST_SUCCESS:
cookie.save('sessionId', action.user.sid, { path: '/' })
return merge({}, state, {
sessionId: action.user.sid,
id: action.user.id,
firstName: action.user.first_name,
lastName: action.user.last_name,
isAuthenticated: true
})
default:
return state
}
}
Right now the reducer responsible for LOGIN_REQUEST_SUCCESS saves the cookie. I know the reducer has to be a pure function.
Is saving a cookie in the reducer violating this principle? Would it be better to save the cookie inside the action creator?
Have a look at redux-persist.
You can persist/save your reducers (or parts of them) in LocalStorage.
Concept
Initiate login.
Receive cookie from server.
Dispatch login success.
Reducer stores cookie in memory.
Persist middleware stores reducer state in LocalStorage.
Example
Install
npm install --save-dev redux-persist
Example Usage
Create a component that wraps the persistence/rehydration logic.
AppProvider.js
import React, { Component, PropTypes } from 'react';
import { Provider } from 'react-redux';
import { persistStore } from 'redux-persist';
class AppProvider extends Component {
static propTypes = {
store: PropTypes.object.isRequired,
children: PropTypes.node
}
constructor(props) {
super(props);
this.state = { rehydrated: false };
}
componentWillMount() {
const opts = {
whitelist: ['user'] // <-- Your auth/user reducer storing the cookie
};
persistStore(this.props.store, opts, () => {
this.setState({ rehydrated: true });
});
}
render() {
if (!this.state.rehydrated) {
return null;
}
return (
<Provider store={this.props.store}>
{this.props.children}
</Provider>
);
}
}
AppProvider.propTypes = {
store: PropTypes.object.isRequired,
children: PropTypes.node
}
export default AppProvider;
Then, in your index.js or file in which you set up the store, wrap the rendered components in your new AppProvider.
index.js
...
import AppProvider from 'containers/AppProvider.jsx';
...
render((
<AppProvider store={store}>
...
</AppProvider>
), document.getElementById('App'));
This will serialize your user reducer state to LocalStorage on each update of the store/state. You can open your dev tools (Chrome) and look at Resources => Local Storage.
I'm not sure if this is the "right" way, but that's how my team is persisting the logged user in the Redux app we built:
We have a very default architecture, an API ready to receive requests in one side, and a React/Redux/Single Page app that consumes this API endpoints in the other side.
When the user credentials are valid, the API's endpoint responsible for the login respond to the app with the user object, including an access token. The access token is latter used in every request made by the app to validate the user against the API.
When the app receives this user information from the API two things happen: 1) an action is dispatched to the users reducer, something like ADD_USER, to include this user in the users store and 2) the user's access token is persisted in the localStorage.
After this, any component can connect to the users reducer and use the persisted access token to know who is the logged user, and of course if you have no access token in your localStorage it means the user is not logged in.
In the top of our components hierarchy, we have one component responsible to connect to the users reducer, get the current user based on the access token persisted in the localStorage, and pass this current user in the React's context. So we avoid every component that depends on the current user to have to connect to the users reducer and read from the localStorage, we assume that this components will always receive the current user from the app's context.
There are some challenges like token expiration that adds more complexity to the solution, but basically this is how we are doing it and it's working pretty well.
I'd probably have the server-side set the cookie, personally, and make it transparent to JavaScript. But if you really want to do it client-side, I'd do it in an action helper. Something like this:
// Using redux-thunk
function login(user, password) {
return dispatch => api.auth.login(user, password)
.then(result => setCookie())
.then(() => dispatch({type: 'USER_LOGGED_IN'}))
}
Or something like that.
Action helpers don't need to be pure, but reducers should be. So, if I'm doing side-effects, I put them into action helpers.
First of all I don't use Ruby nor Devise :) (all my searches led me to plugins that kind of rely on Devise)
I want to do pretty simple authentication with Ember, I have a REST backend that blocks requests without a proper cookie(user-pass) and i want Ember to watch when it gets 403 forbidden (won't let you to transition into protected URLs) and then pop up a user-login dialog.
So when a user tries to send a new message for example(lets say i've built a forum) Ember will fire the request and if it gets 403 it will block the transition and popup a login form and will retry the transition after the login have completed
Also is there a way to get the errors from ember-data and respond to them? (if a user tries to change an attribute he can't access i would like to inform him about it[Access denied or something like that])
I want to use custom errors that my server will send to ember data not just error numbers but words like "Sorry you can't change this before 12 PM"
You can simply listen to the response of your server and transition to your LOGIN (or whatever you call it) route. In my apps I happen to keep two types of routes (LOGIN and AUTHENTICATED). When they access the authenticated routes without logging in, they get a 401 unauthorized error and get transitioned to the LOGIN route.
// AuthenticatedRoute.js
redirectToLogin: function(transition) {
// alert('You must log in!');
var loginController = this.controllerFor('login');
loginController.set('attemptedTransition', transition);
this.transitionTo('login');
},
events: {
error: function(reason, transition) {
if (reason.status === 401) {
this.redirectToLogin(transition);
} else {
console.log(reason);
window.alert('Something went wrong');
}
}
},
model: function () {
return this.store.find('post');
},
So now when the user requests for post he gets a 401 and gets transitioned to LOGIN controller.
// LoginController.js
login: function() {
var self = this, data = this.getProperties('username', 'password');
// Clear out any error messages.
this.set('errorMessage', null);
$.post('/login', data).then(function(response) {
self.set('errorMessage', response.message);
if (response.success) {
alert('Login succeeded!');
// Redirecting to the actual route the user tried to access
var attemptedTransition = self.get('attemptedTransition');
if (attemptedTransition) {
attemptedTransition.retry();
self.set('attemptedTransition', null);
} else {
// Redirect to 'defaultRoute' by default.
self.transitionToRoute('defaultRoute');
}
}
});
}
The basic answer you need is capturing the events in the route and transitioning accordingly. I just happened to include the code for attempted transition as it comes in handy at times.