How am i supposed to use a Auth0 with a RESTful api? - mysql

I'm thinking about using Auth0 to signin my users on my nodejs API.
I'm using a MySQL database to sign them in and i also want to use Facebook so they can register and login.
I'm having an issue with the concept of callback, because my API is not supposed to be accessed via a browser. Only a webapp or a mobile app is supposed to access it. How do i have to implement the handling of my signin/login form inputs on my mobile app to use my API which is supposed to use Auth0 ?
Thank you for your answers.

Auth0 comes with a database on the free account. When you add the login registration widget to your application and a user signs up it adds them to the database in your auth0 account.
You can see information about the process here
What I do is authenticate users with the auth0 widget. This allows auth0 to handle encryption and security. Then when a user logs in i request a profile in the response. Typically this gives me at least basic info like an email address. I create my own database using the email address as a unique key which allows me to serve the correct data to the user when they login.
Here is an example of my auth0 service using a widget and requesting the user's profile in the response then storing it to local storage.
import { Injectable } from '#angular/core';
import { tokenNotExpired, JwtHelper } from 'angular2-jwt';
import { Router } from '#angular/router';
import { myConfig } from './auth.config';
declare var Auth0Lock: any;
var options = {
theme: {
logo: '/img/logo.png',
primaryColor: '#779476'
},
languageDictionary: {
emailInputPlaceholder: "email#example.com",
title: "Login or SignUp"
},
};
#Injectable()
export class Auth {
lock = new Auth0Lock(myConfig.clientID, myConfig.domain, options, {});
userProfile: Object;
constructor(private router: Router) {
this.userProfile = JSON.parse(localStorage.getItem('profile'));
this.lock.on('authenticated', (authResult: any) => {
localStorage.setItem('access_token', authResult.idToken);
this.lock.getProfile(authResult.idToken, (error: any, profile: any) => {
if (error) {
console.log(error);
return;
}
localStorage.setItem('profile', JSON.stringify(profile));
this.userProfile = profile;
this.router.navigateByUrl('/overview');
});
this.lock.hide();
});
}
public login() {
this.lock.show();
}
private get accessToken(): string {
return localStorage.getItem('access_token');
}
public authenticated(): boolean {
try {
var jwtHelper: JwtHelper = new JwtHelper();
var token = this.accessToken;
if (jwtHelper.isTokenExpired(token))
return false;
return true;
}
catch (err) {
return false;
}
}
public logout() {
localStorage.removeItem('profile');
localStorage.removeItem('access_token');
this.userProfile = undefined;
this.router.navigateByUrl('/home');
};
}

Related

React Admin - Redirecting to a page after login

I have a react admin application with a customRoutes, as well as resources (say /users). One of my custom route is a private page (say /private), which I protect with the useAuthenticated() hook:
export default () => {
useAuthenticated();
return (<Card>
<Title title="Private Page" />
<CardContent>
This is a private page.
</CardContent>
</Card>)
}
When I browse this private page and I'm not authenticated, I'm entering an authentication process as it should be (I'm using OIDC). This process is triggered by the checkAuth() method of the authProvider. But when the process is completed, I'm redirected to the /users resource and not the /private page. Is there a way to tell the authProvider that I want to be redirected to the private page?
Thanks - C
I have not done this myself, but I imagine you can use your redirect path as an argument in the useAuthenticated() call. https://marmelab.com/react-admin/Authentication.html#useauthenticated-hook
export default () => {
useAuthenticated({ redirectPath: '/privatepage' });
return (
<Card>
<Title title="Private Page" />
<CardContent>
This is a private page.
</CardContent>
</Card>
)
}
From there you should be able to use that arg/parameter in your checkAuth method.
tl;dr In your login().then() aspect, do a redirect('/myDefaultUrl')
I had the same challenge as you, I think. After looking into the ReactAdmin code a bit I was unable to find a way to do it in any 'official' way. I did see something quite interesting in the useAuthProvider() hook.
The AuthProvider object maintained by RA seems to have a variable with a couple properties initialized with some defaults.
import { useContext } from 'react';
import { AuthProvider } from '../types';
import AuthContext from './AuthContext';
export const defaultAuthParams = {
loginUrl: '/login',
afterLoginUrl: '/',
};
/**
* Get the authProvider stored in the context
*/
const useAuthProvider = (): AuthProvider => useContext(AuthContext);
export default useAuthProvider;
Specifically the afterLoginUrl property looked interesting. I attempted to override that property in my authProvider but didn't have any luck.
What I ended up doing was this. In my invocation of the login (from useLogin()), when the authProvider.login resolves, I return back the user along with their profile (I use Cognito so it is a CognitoUser instance). In my user I have configured an attribute for where the user should go after login. I then simply use the redirect hook (from useRedirect) to then direct the user to that URL.
Here is my login from my AuthProvider:
const authProvider = {
login: (params) => {
if (params instanceof CognitoUser) {
return params;
}
if (params.error) {
return Promise.reject(params.error);
}
return new Promise((resolve, reject) => {
Auth.signIn(params.username,params.password).then(cognitoUser => {
if (cognitoUser.challengeName === 'NEW_PASSWORD_REQUIRED') {
reject({ code: 'NewPasswordRequired', cognitoUser: cognitoUser })
} else {
setAndStoreUserProfile(cognitoUser);
resolve(cognitoUser);
}
}, function(err) {
reject(err);
});
});
},
....
Then in my Login form, I do this:
raLogin(formData)
.then((cognitoUser) => {
console.log("User logged in. Result:", cognitoUser);
clearTransitionState();
redirect('/profile');
})
.catch(error => {
if (error.code && error.code === 'PasswordResetRequiredException') {
transition(PHASE.RESET_ACCOUNT, {username: formData.username });
} else if (error.code && error.code === 'NewPasswordRequired') {
transition(PHASE.NEW_PASSWORD, { cognitoUser: error.cognitoUser });
} else {
processAuthError(error);
}
});

Session Storage does not clear out on logout and does not immediately display result on login Angular 8

The user logs into the application where it checks for the loginID. If validated, the user details are returned through the authentication service. Now I am setting firstName, lastName, and id in sessionStorage to display the name on the sidebar immediately as user logs in. But firstName and lastName are only displayed either on home page refresh else it displays nothing or the previous user's name. On logout, I am removing the items from the sessionStorage.
Login Routing code
this.authenticationService.login(this.username)
.pipe(first())
.subscribe(
data => {
this.router.navigate([this.returnUrl]);
},
Routing Code:
{ path: '', component: HomepageComponent, canActivate: [AuthGuard] },
{ path: 'login', component: LoginComponent, },
{ path: 'homepage', component: HomepageComponent, canActivate: [AuthGuard]},
AuthGuard Code:
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const currentUser = this.authenticationService.currentUserValue;
if (currentUser) {
return true;
}
this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
return false;
}
Code:
function authenticate() {
const { username } = body;
const user = users.find(x => x.username === username);
if (!user) return error('wrong username');
return ok({
id: user.id,
username: user.username,
firstName: user.firstName,
lastName: user.lastName,
token: 'fake-jwt-token'
})
Authentication Service on login and logout. At login, the username is passed to the login method and then sent for validation to authenticate method. The response is then returned.
login(username: string) {
return this.http.post<any>(`${environment.api}/users/authenticate`, { username })
.pipe(map(user => {
sessionStorage.setItem('firstName', JSON.stringify(user.firstName));
sessionStorage.setItem('lastName', JSON.stringify(user.lastName));
sessionStorage.setItem('id', JSON.stringify(user.id));
this.currentUserSubject.next(user);
return user;
}));
}
logout() {
sessionStorage.removeItem('firstName');
sessionStorage.removeItem('lastName');
sessionStorage.removeItem('id');
this.currentUserSubject.next(null);
}
In sidebar typescript:
ngOnInit() {
this.firstName = JSON.parse(sessionStorage.getItem('firstName'));
this.lastName = JSON.parse(sessionStorage.getItem('lastName'));
}
In sidebar HTML:
<p>Welcome {{firstName}} {{lastName}}</p>
I want the sessionStorage to remove the previous details as soon as the user logs out. And next user's name to be displayed as soon as the user logs in. It should not display name on page refresh after entering the page.
It has to do with the ngOninit for the sidebar component. If this is initialised before the storage is set, it will never get a value. Same goes for logging out. The this.firstName value is never updated. What you could do is create a behaviorsubject which returns the user fields and is filled on initialisation or when the user is logging in.
This is the most logical fix for your problem given the code you've provided.
EDIT: Checking more of your code, you already have the subject to subscribe on (currentUserSubject). So if you subscribe to this in your sidebar component it should work.
EDIT 2:
In the authenticationService you have the currentUserSubject subject (i’d change this to a behavoirSubject, will explain later). This has the logged in user for the application. By subscribing to this behaviorSubject you’ll always have the newest value available in the places you subscribe. So also the sidebar component. Upon logging out, you just emit a new empty value to currentUserSubject. This will then clear the sidebar component values.
Upon initialisation of your application you could read your local storage and emit that to your behaviourSubject. This will ensure that if the localstorage is set, the sidebar has it’s values.
The code you will use upon loggin in is behaviorSubject.next(uservalue) and for logging out you could use behaviorSubject.next(null). Then wherever you subscribe you just check wether uservalue is present and handle it accordingly.
The reason to use a behaviorSubject instead of a subject has to do with holding values. The behaviourSubject has to be created with a initial value and will always emit the latest value, while a subject only emits all the values after you’ve subscribed to it.
EDIT 3: Here is the code example
export class AuthService {
private authenticatedUser = new BehaviorSubject<User>(undefined);
constructor(private http: HttpClient) {
this.initSubject();
}
public getAuthenticatedUser = this.authenticatedUser.asObservable();
private initSubject(): void {
if (hasLocalStorage) {
// Fill yourself :) Here you check if there is data in the localStorage
this.authenticatedUser.next(localStorage); // Here you pass in the localstorage data in the same format the login method does.
} else {
this.authenticatedUser.next(undefined);
}
}
login(username: string) {
return this.http
.post<any>(`${environment.api}/users/authenticate`, { username })
.pipe(
map((user) => {
sessionStorage.setItem('firstName', JSON.stringify(user.firstName));
sessionStorage.setItem('lastName', JSON.stringify(user.lastName));
sessionStorage.setItem('id', JSON.stringify(user.id));
this.authenticatedUser.next(user);
return user;
})
);
}
public logOut(): void {
this.authenticatedUser.next(undefined);
// And clear local storage
}
}
export class SidebarComponent implements OnInit {
public firstName: string = '';
public lastName: string = ''
constructor(private authService: AuthService) {}
ngOnInit() {
this.authService.getAuthenticatedUser.subscribe((user) => {
if (user) {
// set firstname and lastname
this.firstName = user.firstName;
this.lastName = user.lastName;
} else {
// not logged in
}
});
}
}

Object users in Observable Angular 8

I created a service and I try call API method (HTTP GET) and I put my data in Observable, I don't understand why I don't see all data(object) from API GET.
angular-component.ts
public allUsers$: Observable<User[]>;
constructor(private usersService: UsersService) { }
ngOnInit() {
this.allUsers$ = this.getAllUsers();
console.log(this.allUsers$)
}
private getAllUsers(): Observable<User[]> {
return this.usersService.getUsers();
}
In console I have this message:
users.service.ts
public getUsers(): Observable<User[]> {
return this.apiService.get(this.type) as Observable<User[]>;
}
api.service.ts
public get(url: string): Observable<any> {
return this.http.get(environment.apiUrl + `/${url}`);
}
nodejs-route.js
app.get("/", async (req, res) => {
const getAllUsers = await User.find().populate("orders.order_id");
res.status(200).send(getAllUsers);
});
Always keep in mind that an Observable does nothing.
As the lead RxJS developer, Ben Lesh, once said:
Observables themselves are enert. They don't stream anything or do
anything. They are templates for streaming/actions/observations that
will be set up on a subscription.
And there are two basic ways to subscribe:
With an async pipe.
Using the subscribe method.
The async pipe in a template AUTOMATICALLY subscribes and unsubscribes for you.
So in your template, you'd have something like this:
<div class="card"
*ngIf="allUsers$ | async as users">
Then you will be able to access users in your template, such as in an *ngFor.
However, using an async pipe makes it a bit more difficult to access the data in your component code. So you can NOT just do this to see your data:
console.log(this.allUsers$)
All that will give you is information on the Observable, as you saw.
The other option is to subscribe in your component:
sub: Subscription
users: User[]
ngOnInit() {
this.sub = this.getAllUsers().subscribe(
users => {
this.users = users;
console.log(users);
);
}
The subscribe() method returns a Subscription that you can then use to manually unsubscribe.
You will then have an array of users User[], NOT an Observable<User[]> as your component property. Your template can then bind to this array.
The first technique (async pipe) is normally the recommended approach.

How to check and store whether user is logged in correctly?

Is there a better way of checking whether a user is logged in? Because I use the following approach for multiple apps and serving them somehow causes disparities, since it confuses the current app's item with other app's items.
I check whether a user is logged in like this:
constructor(private afAuth: AngularFireAuth, private router: Router, private db: AngularFirestore) {
this.userData = new ReplaySubject<UserDetails>();
afAuth.auth.onAuthStateChanged(user => {
if (user) {
this.user = user;
const local = localStorage.getItem('user');
if (local !== null) {
this.userData.next(JSON.parse(localStorage.getItem('user')));
} else {
this.fetchUserData();
}
} else {
localStorage.setItem('user', null);
}
});
}
get isLoggedIn(): boolean {
const user = localStorage.getItem('user');
return user !== 'null';
}
If each app is served from its own domain, then each will have its own localStorage and there can't be any conflict/confusion between them.
If you're serving multiple apps from the same domain, you'll have to use a unique name in the local storage for each app. Something like localStorage.setItem('app1_user', null) vs localStorage.setItem('app2_user', null).
But note that Firebase Authentication only has a single authenticated user per domain. So if you're serving multiple apps from the same domain, the user is (according to Firebase Authentication) signed in to all of them (or to none of them) at the same time.

send data when routing angular 6

i want to show a message in my authentification page when my token expires.
the app has a guard that checks in every request if user is still authenticated or not(token is still valid or not).
my problem is when i used localstorage and session storageto store the variable "expired". expired has always true since it's stored in the navigator. Any other solutions?
I am new to angular so please any help ?
what i want to show in my login page if expired is true:
<span *ngIf="expired" class="help-block text-danger">
Votre session a expiré !
</span>
and here is the guard code :
#Injectable()
export class AuthGuard implements CanActivateChild {
constructor(private router: Router, private localSt:
LocalStorageService, private loginService: LoginService) {
}
canActivateChild(childRoute: ActivatedRouteSnapshot, state:
RouterStateSnapshot) {
if (this.loginService.isAuthenticated()) {
sessionStorage.setItem("expired", 'false');
return true;
}
sessionStorage.setItem("expired", 'true');
this.router.navigate(['/login']);
return false;
}
}
what I do to show "The session has expired" is:
If the user is idle, or navigating to a URL without the proper permissions I send him/her to the login page through a function on a shared service
endSesion( message ) {
window.localStorage.clear(); //to clear all session variables, tokens and stuff
window.localStorage.setItem('message',message) // save as the only left thing in LS
this.router.navigate(['login']);
}
And in the constructor or the ngOnInit of the login component
ngOnInit() {
this.message = window.localStorage.getItem('message'); // get the message from LS
if ( this.message ) {
alert('Dude!: ' + this.message ); //maybe sweetalert will be nicer to look at
}
}
This way you dont only use to throw user out beacuse a session expired but maybe if not permission or maybe if you check another security issue even froma backend response.