I'm using the Slim Framework to develop the backend. But I can not find a way to compare the token generated by my login function:
public function login($request, $response){
$key = $this->container['key'];
$email = $request->getParsedBody()['email'];
$senha = $this->salt . $request->getParsedBody()['senha'];
$usuario = $this->em->getRepository(UsuarioEntity::class)->findOneBy(['email' => $email]);
if(empty($usuario) || !password_verify($senha, $usuario->getSenha())) {
return $response->withJson('Usuario sem permissão de acesso', 401);
}
$token = array(
"session" => password_hash($usuario->getId() . 'f*u87', PASSWORD_BCRYPT),
"id" => $usuario->getId(),
"iat" => time(),
"exp" => time() + (60 * 10)
);
$jwt = \Firebase\JWT\JWT::encode($token, $key);
return $response->withJson($jwt, 200);
}
On the front-end (React) I call a JS class that handles all requests. I get and store the token value, but I do not know how to use it to check if user is logged in or not
Requisition.js
axiosPost(funcao,dados){
//A AUTENTICAÇÃO VAI AQUI
return axios.post(config.urlBase + funcao, dados);
}
setToken(token){
this.token = token;
}
getToken(){
return this.token;
}
LoginEmpresa.js(React Component)
login(){
var reqAxios = new Requisicoes();
reqAxios.axiosPost('login',{ email: this.state.email, senha: this.state.senha }).then(res => {
if(res.data){
reqAxios.setToken(res.data);
}else{
[...]
}
})
}
Thanks
You can check if the JWT is valid by doing a request to the backend API.
public function getUser($request, $response){
$user = // GET CURRENT LOGGED IN USER BASED ON THE JWT
if(!$user) {
return $response->withJson('user is not logged in', 401);
}
return $response->withJson($user, 200);
}
On the React part, you can do a request to the API to get the currently logged in user.
If you receive a 200 response with a user -> logged in
If you receive a 401 response -> not logged in
You can use a response interceptor from Axios to check the status code:
axios.interceptors.response.use(function (response) {
// Do something with response data
return response;
}, function (error) {
// Do something with response error
if (error.status === 401) {
// DELETE YOUR TOKEN
this.removeToken();
}
return Promise.reject(error);
});
Also, I recommend you to store the token in localStorage so that the session of a user won't expire on a page refresh.
setToken(token){
localStorage.setItem('jwt_token', token);
}
getToken(){
return localStorage.getItem('jwt_token');
}
removeToken(){
localStorage.removeItem('jwt_token');
}
As your front end is a React app, on the login response, you should store the token on your app's state. You may have it on the main component of your app or in a redux store, or anywhere else.
It is also good to think about storing the JWT on the localStorage, to ensure the user keeps logged in between multiple tabs on your application.
And if you are using the JWT protocol, you should be configuring your axios instance to send the Authorization HTTP header with the token inside. I don't see it on the piece of code you've provided
Related
I am trying to build a app in ionic framework using angular on below topics i am unable to apply or say i dont know exact procedure for implementing that below are my key issues:
How can i can check the token expiry date so that if token is expired i can logout the user .
How can i maintain the token through out the application using storage right now i manually calling the constructor in the service ?
How can i toggle the url based on the token i.e if token is there i have to send x/url if token is not there i have to send y/url
what i have done
Login.page.ts
this.Auth.authenticate(this.loginForm.value).then((res: any) => {
this.loading = false;
if (res.idToken.payload['cognito:groups']) {
if (res.idToken.payload['cognito:groups'].length > 0) {
admin = 'true';
} else {
admin = 'false';
}
} else {
admin = 'false';
}
this.storage.set('authToken',res.accessToken.jwtToken);
this.storage.set('isLogged',true);
this.navCtrl.navigateBack('/tabs/tab1');
}).catch((err => {
this.loading = false;
this.toastServ.showToast(err.message, 'error');
}));
"http.interceptor.ts"
export class HttpConfigInterceptor implements HttpInterceptor {
token:any;
constructor(public storage: Storage ) {
console.log('http interceptor');
this.storage.get('authToken').then(data => {
this.token = data;
});
}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
console.log('intercept');
if (this.token) {
request = request.clone({ headers: request.headers.set('Authorization', this.token) });
}
if (!request.headers.has('Content-Type')) {
request = request.clone({ headers: request.headers.set('Content-Type', 'application/json') });
}
request = request.clone({ headers: request.headers.set('Accept', 'application/json') });
return next.handle(request).pipe(
map((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
}
return event;
}));
}
}
user.service.ts
right now i am checking the token like this
checkUserToken(){
this.storage.get('authToken').then(data => {
this.url = this.url + 'someotherurl/';
this.token = data;
});
}
getLatLong(): Observable<any>{
this.checkUserToken();
const endPoint = this.url + 'availableData';
return this.http.get(endPoint).pipe(map(response => {
return response;
}), catchError(error => {
return 'Unable establish connection!';
}));
}
1) How can i can check the token expiry date so that if token is expired i can logout the user
When you login and as for the token, it will return the expiry data too inside the object. What you can do is to store the expiry date in the Local Storage too so you can check it and handle it. For example, you can check for the expiry date when you start the app, you will have three possibilities:
There is neither a token nor an expiry date (first time), so you will go to the login page.
The token has been expired as the expiry date is less than the current date. In this case you will need to go to the login page too to get a new one.
The token is valid and the expiry date is greater than the current date. You have a session so there is no need to go to the login page. You can set a timeout with the difference between the two dates and when that timeout comes, you will display a popup to the user and go to login page. For example, if the current time is 09:00 and the expiry is 09:15, you will create a timeout with 15 mins that will show a popup to user once passed.
2) How can i maintain the token through out the application using storage right now i manually calling the constructor in the service ?
You can keep it in a central singleton service that you can always get / update the token using it. You can have an init method that will be called in the App Component to initialize the above logic.
3) How can i toggle the url based on the token i.e if token is there i have to send x/url if token is not there i have to send y/url
From the above logic, you can easily differentiate between the two states. So you can choose which API you want to call easily.
I have made a full stack application with a register activity successfully adding to db.
How would I conditionally render the home page dependent on if the login is correct.
In my login route I have an if statement which successfully logs "bad creds" if do not exist or "login: login successful.." if it does.
I added a redirect into the handle submit(this is triggered once the login form button is pressed) which was supposed to be triggered if successful (it technically is but it determines "bad creds successful as well").
I have attempted an if stametn but I am not sure how to use this with express middle ware.
the logic I would want the the portion of handle submit to do is something along the lines of
if (login successful){
window.location.href = "/home";
}
else {
window.location.href = "/login";
(preferably with a alert )
}
Login route
app.post("/login", async (req, response) => {
try {
await sql.connect(config);
var request = new sql.Request();
var Email = req.body.email;
var Password = req.body.password;
console.log({ Email, Password });
request.input("Email", sql.VarChar, Email);
request.input("Password", sql.VarChar, Password);
var queryString =
"SELECT * FROM TestLogin WHERE email = #Email AND password = #Password";
//"SELECT * FROM RegisteredUsers WHERE email = #Email AND Password = HASHBYTES('SHA2_512', #Password + 'skrrt')";
const result = await request.query(queryString);
if (result.recordsets[0].length > 0) {
console.info("/login: login successful..");
console.log(req.body);
req.session.loggedin = true;
req.session.email = Email;
response.send("User logined");
} else {
console.info("/login: bad creds");
response.status(400).send("Incorrect email and/or Password!");
}
} catch (err) {
console.log("Err: ", err);
response.status(500).send("Check api console.log for the error");
}
});
handleSubmit(e) {
e.preventDefault();
if (this.state.email.length < 8 || this.state.password.length < 8) {
alert(`please enter the form correctly `);
} else {
const data = { email: this.state.email, password: this.state.password };
fetch("/login", {
method: "POST", // or 'PUT'
headers: {
Accept: "application/json, text/plain, */*",
"Content-Type": "application/json"
},
body: JSON.stringify(data)
})
// .then(response => response.json())
.then(data => {
console.log("Success:", data);
// if ( ) {
// console.log("nice");
// } else {
// console.log("not nice");
// }
// window.location.href = "/home";
})
.catch(error => {
console.error("Error:", error);
});
}
}
catch(e) {
console.log(e);
}
You should have explained in the first place that you had a React app in the frontend. Talking at the same time about Express middleware and login route is a bit messy. :)
What you're doing is a login/sign in process through an API. This means your server should return JSON information regarding the login outcome. Then, your frontend should handle that in whatever way you want to. This means that your server should simply treat the login request as any other data request. Return a status code and some optional JSON data.
Authentication is a BIG subject and since you did't provide many details, I can only tell you how normally the overall process should go:
Send the user credentials to the server (like you do in your POST request)
Handle the response received from the server. If login was successful, you should receive some information from the server, like the user id, email, session id, either in the response JSON data or by HTTP headers. You should keep this information in the frontend, normally in localStorage, and use it for every request to the server to provide your identity. You should look up JSON Web Tokens.
In your React app, you want to check when starting the application if the user is already logged in or not (using the piece of information mentioned in step 2, or trying to fetch and endpoint that returns user information like /me). If you don't have that information or the request fails, redirect to Login.
In your React app, in your login page, handle the fetch result and redirect to home if the user is authenticated, or stay there and display whatever info you want.
I assume that since you're using user login some resources should be protected from being accessed by non logged in users or restricted depending on the logged in user. This is done with middleware on your Express server, that should check the user id / token / session id information your React app should be sending with every request.
To redirect using React Router, you don't want to use window.location. You want to use the Router itself to avoid reloading the full page. You can either use the injected history prop on your Login route component or wrap any component that needs it with withRouter HOC.
This article seem to lay out all options using React Router pretty well:
https://serverless-stack.com/chapters/redirect-on-login-and-logout.html
Hope this helps, this is a complex subject that you should split into smaller problems and tackle one at a time. ;)
I'm using AWS for my website. After 1 hour the token expires and the user pretty much can't do anything.
For now i'm trying to refresh the credentials like this:
function getTokens(session) {
return {
accessToken: session.getAccessToken().getJwtToken(),
idToken: session.getIdToken().getJwtToken(),
refreshToken: session.getRefreshToken().getToken()
};
};
function getCognitoIdentityCredentials(tokens) {
const loginInfo = {};
loginInfo[`cognito-idp.eu-central-1.amazonaws.com/eu-central-1_XXX`] = tokens.idToken;
const params = {
IdentityPoolId: AWSConfiguration.IdPoolId
Logins: loginInfo
};
return new AWS.CognitoIdentityCredentials(params);
};
if(AWS.config.credentials.needsRefresh()) {
clearInterval(messwerte_updaten);
cognitoUser.refreshSession(cognitoUser.signInUserSession.refreshToken, (err, session) => {
if (err) {
console.log(err);
}
else {
var tokens = getTokens(session);
AWS.config.credentials = getCognitoIdentityCredentials(tokens);
AWS.config.credentials.get(function (err) {
if (err) {
console.log(err);
}
else {
callLambda();
}
});
}
});
}
the thing is, after 1hour, the login token gets refreshed without a problem, but after 2hrs i can't refresh the login token anymore.
i also tried using AWS.config.credentials.get(), AWS.config.credentials.getCredentials() and AWS.config.credentials.refresh()
which doesn't work either.
The error messages i'm getting are:
Missing credentials in config
Invalid login token. Token expired: 1446742058 >= 1446727732
After almost 2 weeks i finally solved it.
You need the Refresh Token to receive a new Id Token. Once the Refreshed Token is acquired, update the AWS.config.credentials object with the new Id Token.
here is an example on how to set this up, runs smoothly!
refresh_token = session.getRefreshToken(); // you'll get session from calling cognitoUser.getSession()
if (AWS.config.credentials.needsRefresh()) {
cognitoUser.refreshSession(refresh_token, (err, session) => {
if(err) {
console.log(err);
}
else {
AWS.config.credentials.params.Logins['cognito-idp.<YOUR-REGION>.amazonaws.com/<YOUR_USER_POOL_ID>'] = session.getIdToken().getJwtToken();
AWS.config.credentials.refresh((err)=> {
if(err) {
console.log(err);
}
else{
console.log("TOKEN SUCCESSFULLY UPDATED");
}
});
}
});
}
Usually it's solved by intercepting http requests with additional logic.
function authenticationExpiryInterceptor() {
// check if token expired, if yes refresh
}
function authenticationHeadersInterceptor() {
// include headers, or no
}}
then with use of HttpService layer
return HttpService.get(url, params, opts) {
return authenticationExpiryInterceptor(...)
.then((...) => authenticationHeadersInterceptor(...))
.then((...) => makeRequest(...))
}
It could be solved by proxy as well http://2ality.com/2015/10/intercepting-method-calls.html
In relation to AWS:
https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Credentials.html
You're interested in:
getPromise()
refreshPromise()
Here is how I implemented this:
First you need to authorize the user to the service and grant permissions:
Sample request:
Here is how I implemented this:
First you need to authorize the user to the service and grant permissions:
Sample request:
POST https://mydomain.auth.us-east-1.amazoncognito.com/oauth2/token&
Content-Type='application/x-www-form-urlencoded'&
Authorization=Basic aSdxd892iujendek328uedj
grant_type=authorization_code&
client_id={your client_id}
code=AUTHORIZATION_CODE&
redirect_uri={your rediect uri}
This will return a Json something like:
HTTP/1.1 200 OK
Content-Type: application/json
{"access_token":"eyJz9sdfsdfsdfsd", "refresh_token":"dn43ud8uj32nk2je","id_token":"dmcxd329ujdmkemkd349r", "token_type":"Bearer", "expires_in":3600}
Now you need to get an access token depending on your scope:
POST https://mydomain.auth.us-east-1.amazoncognito.com/oauth2/token
Content-Type='application/x-www-form-urlencoded'&
Authorization=Basic aSdxd892iujendek328uedj
grant_type=client_credentials&
scope={resourceServerIdentifier1}/{scope1} {resourceServerIdentifier2}/{scope2}
Json would be:
HTTP/1.1 200 OK
Content-Type: application/json
{"access_token":"eyJz9sdfsdfsdfsd", "token_type":"Bearer", "expires_in":3600}
Now this access_token is only valid for 3600 secs, after which you need to exchange this to get a new access token. To do this,
To get new access token from refresh Token:
POST https://mydomain.auth.us-east-1.amazoncognito.com/oauth2/token >
Content-Type='application/x-www-form-urlencoded'
Authorization=Basic aSdxd892iujendek328uedj
grant_type=refresh_token&
client_id={client_id}
refresh_token=REFRESH_TOKEN
Response:
HTTP/1.1 200 OK
Content-Type: application/json
{"access_token":"eyJz9sdfsdfsdfsd", "refresh_token":"dn43ud8uj32nk2je", "id_token":"dmcxd329ujdmkemkd349r","token_type":"Bearer", "expires_in":3600}
You get the picture right.
If you need more details go here.
This is how you can refresh access token using AWS Amplify library:
import Amplify, { Auth } from "aws-amplify";
Amplify.configure({
Auth: {
userPoolId: <USER_POOL_ID>,
userPoolWebClientId: <USER_POOL_WEB_CLIENT_ID>
}
});
try {
const currentUser = await Auth.currentAuthenticatedUser();
const currentSession = currentUser.signInUserSession;
currentUser.refreshSession(currentSession.refreshToken, (err, session) => {
// do something with the new session
});
} catch (e) {
// whatever
}
};
More discussion here: https://github.com/aws-amplify/amplify-js/issues/2560.
I would like to know if it is possible to do this, because I'm not sure if I'm wrong or if it isn't possible. Basically, what I want to do is to create a wrap function for native fetch javascript function. This wrap function would implement token validation process, requesting a new accessToken if the one given is expired and requesting again the desired resource. This is what I've reached until now:
customFetch.js
// 'url' and 'options' parameters are used strictely as you would use them in fetch. 'authOptions' are used to configure the call to refresh the access token
window.customFetch = (url, options, authOptions) => {
const OPTIONS = {
url: '',
unauthorizedRedirect: '',
storage: window.sessionStorage,
tokenName: 'accessToken'
}
// Merge options passed by user with the default auth options
let opts = Object.assign({}, OPTIONS, authOptions);
// Try to update 'authorizarion's header in order to send always the proper one to the server
options.headers = options.headers || {};
options.headers['Authorization'] = `Bearer ${opts.storage.getItem(opts.tokenName)}`;
// Actual server request that user wants to do.
const request = window.fetch(url, options)
.then((d) => {
if (d.status === 401) {
// Unauthorized
console.log('not authorized');
return refreshAccesToken();
}
else {
return d.json();
}
});
// Auxiliar server call to get refresh the access token if it is expired. Here also check if the
// cookie has expired and if it has expired, then we should redirect to other page to login again in
// the application.
const refreshAccesToken = () => {
window.fetch(opts.url, {
method: 'get',
credentials: 'include'
}).then((d) => {
// For this example, we can omit this, we can suppose we always receive the access token
if (d.status === 401) {
// Unauthorized and the cookie used to validate and refresh the access token has expired. So we want to login in to the app again
window.location.href = opts.unauthorizedRedirect;
}
return d.json();
}).then((json) => {
const jwt = json.token;
if (jwt) {
// Store in the browser's storage (sessionStorage by default) the refreshed token, in order to use it on every request
opts.storage.setItem(opts.tokenName, jwt);
console.log('new acces token: ' + jwt);
// Re-send the original request when we have received the refreshed access token.
return window.customFetch(url, options, authOptions);
}
else {
console.log('no token has been sent');
return null;
}
});
}
return request;
}
consumer.js
const getResourcePrivate = () => {
const url = MAIN_URL + '/resource';
customFetch(url, {
method: 'get'
},{
url: AUTH_SERVER_TOKEN,
unauthorizedRedirect: AUTH_URI,
tokenName: TOKEN_NAME
}).then((json) => {
const resource = json ? json.resource : null;
if (resource) {
console.log(resource);
}
else {
console.log('No resource has been provided.');
}
});
}
I'll try to explain a little better the above code: I want to make transparent for users the token validation, in order to let them just worry about to request the resource they want. This approach is working fine when the accessToken is still valid, because the return request instruction is giving to the consumer the promise of the fetch request.
Of course, when the accessToken has expired and we request a new one to auth server, this is not working. The token is refreshed and the private resource is requested, but the consumer.js doesn't see it.
For this last scenario, is it possible to modify the flow of the program, in order to refresh the accessToken and perform the server call to get the private resource again? The consumer shouldn't realize about this process; in both cases (accessToken is valid and accessToken has expired and has been refreshed) the consumer.js should get the private requested resource in its then function.
Well, finally I've reached a solution. I've tried to resolve it using a Promise and it has work. Here is the approach for customFetch.js file:
window.customFetch = (url, options, authOptions) => {
const OPTIONS = {
url: '',
unauthorizedRedirect: '',
storage: window.sessionStorage,
tokenName: 'accessToken'
}
// Merge options passed by user with the default auth options
let opts = Object.assign({}, OPTIONS, authOptions);
const requestResource = (resolve) => {
// Try to update 'authorizarion's header in order to send always the proper one to the server
options.headers = options.headers || {};
options.headers['Authorization'] = `Bearer ${opts.storage.getItem(opts.tokenName)}`;
window.fetch(url, options)
.then((d) => {
if (d.status === 401) {
// Unauthorized
console.log('not authorized');
return refreshAccesToken(resolve);
}
else {
resolve(d.json());
}
});
}
// Auxiliar server call to get refresh the access token if it is expired. Here also check if the
// cookie has expired and if it has expired, then we should redirect to other page to login again in
// the application.
const refreshAccesToken = (resolve) => {
window.fetch(opts.url, {
method: 'get',
credentials: 'include'
}).then((d) => {
if (d.status === 401) {
// Unauthorized
window.location.href = opts.unauthorizedRedirect;
}
return d.json();
}).then((json) => {
const jwt = json.token;
if (jwt) {
// Store in the browser's storage (sessionStorage by default) the refreshed token, in order to use it on every request
opts.storage.setItem(opts.tokenName, jwt);
console.log('new acces token: ' + jwt);
// Re-send the original request when we have received the refreshed access token.
requestResource(resolve);
}
else {
console.log('no token has been sent');
return null;
}
});
}
let promise = new Promise((resolve, reject) => {
requestResource(resolve);
});
return promise;
}
Basically, I've created a Promise and I've called inside it to the function which calls to server to get the resource. I've modified a little the request(now called requestResource) and refreshAccessToken in order to make them parametrizable functions. And I've passed to them the resolve function in order to "resolve" any function once I've received the new token.
Probably the solution can be improved and optimized, but as first approach, it is working as I expected, so I think it's a valid solution.
EDIT: As #Dennis has suggested me, I made a mistake in my initial approach. I just had to return the promise inside the refreshAccessToken function, and it would worked fine. This is how the customFetch.js file should look (which is more similar to the code I first posted. In fact, I've just added a return instruction inside the function, although removing the start and end brackets would work too):
// 'url' and 'options' parameters are used strictely as you would use them in fetch. 'authOptions' are used to configure the call to refresh the access token
window.customFetch = (url, options, authOptions) => {
const OPTIONS = {
url: '',
unauthorizedRedirect: '',
storage: window.sessionStorage,
tokenName: 'accessToken'
}
// Merge options passed by user with the default auth options
let opts = Object.assign({}, OPTIONS, authOptions);
// Try to update 'authorizarion's header in order to send always the proper one to the server
options.headers = options.headers || {};
options.headers['Authorization'] = `Bearer ${opts.storage.getItem(opts.tokenName)}`;
// Actual server request that user wants to do.
const request = window.fetch(url, options)
.then((d) => {
if (d.status === 401) {
// Unauthorized
console.log('not authorized');
return refreshAccesToken();
}
else {
return d.json();
}
});
// Auxiliar server call to get refresh the access token if it is expired. Here also check if the
// cookie has expired and if it has expired, then we should redirect to other page to login again in
// the application.
const refreshAccesToken = () => {
return window.fetch(opts.url, {
method: 'get',
credentials: 'include'
}).then((d) => {
// For this example, we can omit this, we can suppose we always receive the access token
if (d.status === 401) {
// Unauthorized and the cookie used to validate and refresh the access token has expired. So we want to login in to the app again
window.location.href = opts.unauthorizedRedirect;
}
return d.json();
}).then((json) => {
const jwt = json.token;
if (jwt) {
// Store in the browser's storage (sessionStorage by default) the refreshed token, in order to use it on every request
opts.storage.setItem(opts.tokenName, jwt);
console.log('new acces token: ' + jwt);
// Re-send the original request when we have received the refreshed access token.
return window.customFetch(url, options, authOptions);
}
else {
console.log('no token has been sent');
return null;
}
});
}
return request;
}
So I am working on an angular2 application and am trying to generate a JWT after logging in so that a user's profile information can be obtained. I can successfully login and that's when I generate the token. After logging in I route the user to the profile page and that's where I call my api to get the user information. All of this only works after I login and refresh to page.
this.auth_service
.Login(this.email, this.password)
.subscribe(
data => {
this.global_events.change_nav_bar.emit(true)
console.log('logged in successfully: ' + data)
this.router.navigate(['/profile'])
})
// auth_service above calls this method
Login(email, password): Observable<boolean>
{
return this.http
.post('/api/login', JSON.stringify({email: email, password: password}), this.Get_Headers('no_auth'))
.map(Handle_Response)
function Handle_Response(response: Response)
{
let token = response.json() && response.json().token
if(token)
{
this.token = token
localStorage.setItem('current_user', JSON.stringify({ email: email, token: token }))
return true
}
else
return false
}
}
// Then in my profile component I do this. I have experimented with different timeout times.
ngOnInit(): void
{
this.global_events.change_nav_bar.emit(true)
setTimeout(() => this.Get_User_Data(), 10000)
}
I solved this (not really solved but found another solution) by just pulling the token directly from localStorage instead of setting in the authenticationService