I have this requirement for my website. The website will be a SPA built with Angular 2.
What I want is that the user can login using Google, Facebook, Twitter oauth.
I can setup the oauth on the node server, but the problem is how do I allow login through angular 2 app.
In the angular 2 app if when the user clicks facebook signin option, I cant redirect the user as then I lose the state of my application.
I dont know if this is a very beginner problem. Can anyone help me with the flow of what happens in angular 2 + node oauth
You are going to want to setup routes in the angular app to handle the front end of your application. Then create a service to handle the auth0 authentication of the application,
This is a overview of setting up a secure set of routes and a public set of routes in your app. Once someone logs in with oauth they will be forwarded to the secure routes.
So starting out here is the routes. We will specify a secure and public in the app.routing.ts file
Routes
const APP_ROUTES: Routes = [
{ path: '', redirectTo: '/home', pathMatch: 'full', },
{ path: '', component: PublicComponent, data: { title: 'Public Views' }, children: PUBLIC_ROUTES },
{ path: '', component: SecureComponent, canActivate: [Guard], data: { title: 'Secure Views' }, children: SECURE_ROUTES }
];
Ok so now that you have that. You can create a templates directory. Inside create secure.component and public.component. Then I create a directory called secure and one called public which I put all of my components dependent on the authentication level to access them. I also add their routes to a file in those directories to keep everything separate.
Notice in my routes above I have the [Guard] setup on the secure. This will block anyone from going to the secure routes without authentication.
Here is an example of what that guard looks like.
import { Injectable } from '#angular/core';
import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '#angular/router';
import { Auth } from './auth.service';
import { Observable } from 'rxjs/Observable';
#Injectable()
export class Guard implements CanActivate {
constructor(protected router: Router, protected auth: Auth ) {}
canActivate() {
if (localStorage.getItem('access_token')) {
// logged in so return true
return true;
}
// not logged in so redirect to login page
this.router.navigate(['/home']);
return false;
}
}
Now that we have that we have the routes secured with Guard. We can setup the auth0 client.
Create a config file with your credentials you get from auth0
interface AuthConfiguration {
clientID: string,
domain: string,
callbackURL: string
}
export const myConfig: AuthConfiguration = {
clientID: 'clietnifherefromauth0',
domain: 'username.auth0.com',
// You may need to change this!
callbackURL: 'http://localhost:3000/endpoint/'
};
Then to actually authenticate someone. Receive their data and save the token as well as their data to the local storage. Also provide a logout function and a check to make sure they are logged in.
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('/CHANGETHISTOYOURROUTE');
});
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');
};
}
Make sure to go into your auth0 dashboard and select the social links you want. In your case facebook twitter and Google. Then when someone activates the widget those three will appear.
So all we have to do now is show the widget when someone clicks login,
html will show a login link. But if they are logged in it will show a bit of information about them instead.
<ul class="nav navbar-nav pull-right">
<li class="nav-item">
<a class="nav-link" (click)="auth.login()" *ngIf="!auth.authenticated()">Login / SignUp</a>
<a class="aside-toggle" href="#" role="button" aria-haspopup="true" aria-expanded="false" *ngIf="auth.authenticated()">
<span *ngIf="auth.authenticated() && auth.userProfile" class="profile-name">{{auth.userProfile.nickname}}</span>
<span *ngIf="!auth.authenticated() && !auth.userProfile" class="profile-name">Account</span>
<i class="icon-bell"></i><span class="tag tag-pill tag-danger profile-alerts">5</span>
<img *ngIf="auth.authenticated() && auth.userProfile" [src]="auth.userProfile.picture" class="img-avatar profile-picture" alt="User profile picture">
<img *ngIf="!auth.authenticated() && !auth.userProfile" src="/img/avatars/gravatar-default.png" alt="Default profile-picture">
</a>
</li>
</ul>
Let me know if anything is not clear. I would be glad to help.
Related
I create belowed decorator to get current logged in to the system user,
export const CurrentUser = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.user;
},
);
but I do not want to use this because i need to use in any of my controller
which is a bit troublesome for me because some functions are optional, i.e. both for the logged in user and the non logged in user,
so, how can I get current logged in user in my service in functions when i want to get current user instead of all via decorator in controller?
thanks for any help
You'd have to make a custom provider and inject the request into it. Something like this
{
provider: 'CURRENT_USER',
inject: [REQUEST],
useFactory: (req: Request) => {
return req.user;
},
scope: Scope.REQUEST,
}
(REQUEST is injected from #nestjs/core)
Then the user can be injected into the service with #Inject('CURRENT_USER'). Keep in mind, this will make the service REQUEST scoped, and by scope hierarchy it will make whatever you inject the service into REQUEST scoped.
Edit 2/15/21
An example of this module could look something like this:
#Module({
providers: [{
provider: 'CURRENT_USER',
inject: [REQUEST],
useFactory: (req: Request) => {
return req.user;
},
scope: Scope.REQUEST,
}],
exports: ['CURRENT_USER'],
})
export class CurrentUserModule {}
And now in whatever module that has the service that needs the current user you do
#Module({
imports: [CurrentUserModule],
providers: [ServiceThatNeedsUser],
})
export class ModuleThatNeedsUser {}
and in the service:
#Injectable()
export class ServiceThatNeedsUser {
constructor(#Inject('CURRENT_USER') private readonly user: UserType) {}
// rest of class implementation
}
I have an angular application and added custom data to the route given as
{ path: 'profile', component: ProfileComponent, data: {title: 'example'} }
and in app.component.ts file I have the following code as
private route: ActivatedRoute
console.log('route snapshot', this.route.firstChild.data._value.pageType );
the error i am getting is
ERROR in src/app/app.component.ts(71,64): error TS2339: Property '_value' does not exist on type 'Observable<Data>'.
can anyone tell me how to access data of route in app.component.ts
Update
You will need to subscribe to the router events in the AppComponent and get the params from it:
constructor(private router: Router, route: ActivatedRoute) { }
ngOnInit() {
this.router.events.subscribe(event => {
if(event instanceof NavigationEnd){
console.log(this.route.root.firstChild.snapshot.data['title']);
}
});
}
If you want it only for that path, you can add one more condition
if(event instanceof NavigationEnd && event.url === '/profile')
Old Answer
You will need to access the snapshot to get the data params
this.route.snapshot.data['title'];
I have Login/Create Account in My application. I want the user will not be able to see login/create account if user is already logged in.
I am using an Authguard to protect the route:
#Injectable()
export class AuthGuard implements CanActivate {
constructor(private router: Router) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {debugger;
if (localStorage.getItem('UserLoggedIn')) {
// logged in so return true
this.router.navigate(['']);
return true;
}
// not logged in so redirect to login page with the return url
this.router.navigate(['login']);
return false;
}
In this case the page is going in infinite loop.
This is my Routes:
const appRoutes: Routes = [
{ path: '', component: HomeComponent , data: { animation: '' }},
{ path: 'login', component: UserloginComponent , data: { animation: 'login' },canActivate:[AuthGuard]},
{ path: 'create-account', component: CreateAccountComponent, data: { animation: 'create-account' } },
{ path: 'forgot-password', component: ForgotPasswordComponent, data: { animation: 'forgot-password' } },
{ path: '**', component: PageNotfoundComponent }
];
Please Help. I want it for login and create account.
It is because you added this.router.navigate(['login']); to your authguard and this authguard was attached to login route. Each time a route was accessed it always call all the guards that was attached. So in your case if you access login, it will infinitely redirect to login. There are many ways to solve your issue. If you are intending to add the guard on the login route, just remove this.router.navigate(['login']); to avoid infinite loop. But i suggest to add the guard only to those routes you want to protect being accessed at if the user is not logged in.
Try this:
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {debugger;
let redirect: boolean = false;
if (localStorage.getItem('UserLoggedIn')) {
// logged in so return true
this.router.navigate(['']);
redirect = true;
} else {
// not logged in so redirect to login page with the return url
this.router.navigate(['login']);
redirect = false;
}
return redirect;
}
I am trying to figure out how to pass the value from the RouterStateSnapshot in my auth.guard file to my routing file in my Angular 2 app. I want to do this because, rather than loading a hard-coded default component first, I want, after re-login, for the last active component/page to load up. I have this value in my canActivate() function in my AuthGuard file, because I can console out it out via RouterStateSnapshot. So now I need to figure out how to pass this value on to my root routing file so it, on login/re-login, that component gets loaded.
This is the canActivate() function in my AuthGuard file:
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot)
{
// Get route content id
let contentId = Object.getPropertyValueAtPath(route, 'data.contentId');
console.log(state.url);
// If route does not have session id, don’t load in tab view
if (!String.isNotNullOrEmpty(contentId))
{
console.error(`Route (${route.routeConfig.path}) does not have a content id.`);
this.router.navigateByUrl(''); // Forward to default page.
this.router.navigate([state.url]);
return false;
}
if (this.disabled) return true;
if (sessionStorage.getItem('currentUser'))
{
// logged in so return true
return true;
}
// not logged in so redirect to login page with the return url
this.router.navigate(['/login', {returnUrl: state.url}]);
return false;
}
Notice that I am doing this within that function: console.log(state.url). This gives me the correct value. Now I need to pass it to my app-routing file.
To clarify, currently, on re-login, the last active component is loaded -- but it displays as a background tab, and the default 'redirect' component is what loads up as the active component (i.e, it shows as the active tab).
A simplified version of the app-routing file looks like this:
import { HomeComponent } ...
export const routes: Routes = [
{ path: 'login', component: LoginComponent },
{ path: 'home', component: HomeComponent },
{ path: 'about', component: AboutComponent },
{ path: '**', redirectTo: 'home' }
];
As you can see above, on initial load I currently redirect the user to the 'home component' by default. What I'd like to do is re-direct them to the value that is stored in "state.url" from RouterStateSnapshot. I'm not clear how to do this, however. Any ideas as to how I'd pass that value from my AuthGuard file down to my app-routing file? Can I simply inject RouterStateSnapshot into my app-routing file to get that desired value directly? Or can I use "resolve" here along with the path in routing? What's the recommended way to handle this kind of scenario?
I accomplish this by storing the url in a shared service from my AuthGuard
// auth-guard.ts
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
let isLoggedIn = this.authService.isUserLoggedIn();
if(isLoggedIn){
return true;
}else{
this.someService.storeRedirectUrl(state.url);
this.router.navigateByUrl('/login');
return false;
}
}
Then when the user logs in, check if that redirect url was stored and navigate to it
// login method within login page
login(){
this.authService.login(email, password).subscribe(
res => {
// successful user login, so determine where to route user
if(this.someService.redirectUrl){
// redirect url found, navigate to that url
this.router.navigateByUrl(this.someService.redirectUrl);
}else{
// if no redirect url found, navigate to normal landing page
this.router.navigateByUrl('/home');
}
});
}
Routes File
// routes
export const routes: Routes = [
{
path: 'login',
component: LoginComponent
},
{
path: 'home',
component: HomeComponent,
canActivate: [AuthGuard]
},
{
path: 'about',
component: AboutComponent,
canActivate: [AuthGuard]
},
{
path: '**',
redirectTo: 'home'
}
];
Can I simply inject RouterStateSnapshot into my app-routing file to get that desired value directly?
app-routing is just for mapping routes to components, so there is no injecting the route snapshot into it.
Another option you could do is to pass the redirect url as a query parameter of the login page within the auth guard. (I think this was what you were heading towards)
// auth-guard.ts
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
let isLoggedIn = this.authService.isUserLoggedIn();
if(isLoggedIn){
return true;
}else{
this.router.navigate(['/login', {redirectUrl: state.url}]);
return false;
}
}
Then the process is the same after a user logs in successfully, except this time you fetch the redirect url from the url parameters instead of the service.
I'm using ReactJS and react-router in my application. I need to do next thing: when user open the page - to check, if he logged in or no, and if yes, then to do redirect to /auth page. I'm doing next thing:
componentWillMount: function() {
if (sessionStorage["auth"] == null) {
this.transitionTo("/auth");
}
},
In browser url it really do redirect to /auth, render the /auth page and after override it with current component. So here the question: how to cancel rendering for the current component, or how to do redirect in other way? Thanks.
Take a look at the react-router example auth-with-shared-root: https://github.com/rackt/react-router/tree/master/examples/auth-with-shared-root
What they basically do is to check on an enter of a route where to go. Take a look in file config/routes.js line 36, 51 and 89:
{ onEnter: redirectToLogin,
childRoutes: [
// Protected nested routes for the dashboard
{ path: '/page2',
getComponent: (location, cb) => {
require.ensure([], (require) => {
cb(null, require('../components/PageTwo'))
})
}
}
// other protected routes...
]
}
This route will call function redirectToLogin when the route /page2 is entered or pushed to. The function can than check, if the user is authenticated with auth.loggedIn()
function redirectToLogin(nextState, replace) {
if (!auth.loggedIn()) {
replace({
pathname: '/login',
state: { nextPathname: nextState.location.pathname }
})
}
}
If the user is not logged in, the function will replace the route to /login, where the user can authenticates itself.