Angular2 redirect user to somewhere when JWT token expires and he currently is on guarded location - angular2-routing

i'm using angular2-jwt package from auth0. Everything works fine, but now i am wondering how to redirect user, who is currently on some path, which is guarded with my auth guard, somewhere else. Now it is redirecting just when is user trying to get to the guarded path.
I know that i can hide objects in my component, but then i must do that in every single guarded component, which is not so clear.
Here are my routes:
const appRoutes: Routes = [
{path: 'login', component: LoginComponent},
{path: 'register', component: RegisterComponent},
{path: '', component: HomeComponent},
{path: 'user/edit', component: EditProfileComponent, canActivate: [AuthGuard]},
];
and this is my guard service:
...
#Injectable()
export class AuthGuard implements CanActivate {
constructor(private auth: AuthService, private router: Router) {
}
canActivate() {
if (this.auth.loggedIn()) {
return true;
}
this.router.navigate(['/']);
return false;
}
}

Http interceptor
What you can do is implement a HTTP interceptor.
This HTTP interceptor can check for error codes like 401 (not authenticated) and/or 403 (fobidden, not authorized to access). With an implementation like this, you can also set the authorization headers of your requests each time you send a request to the server.
Make sure you are using HttpClient inside your application. The interceptor listens only for requests made by HttpClient.
Implementation
Step 1, create an error interceptor:
import { Router } from '#angular/router';
import { Observable } from 'rxjs/Observable';
import { Injectable } from '#angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '#angular/common/http';
import { HttpErrorResponse } from '#angular/common/http';
import 'rxjs/add/operator/do';
#Injectable()
export class ErrorInterceptor implements HttpInterceptor {
constructor(private router: Router) { }
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).do(event => { }, err => {
if (err instanceof HttpErrorResponse && err.status == 401) {
this.router.navigateByUrl('/login');
} else if (err instanceof HttpErrorResponse && err.status == 403) {
this.router.navigate(['/not-authorized']);
}
});
}
}
Step 2, use the error interceptor:
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: ErrorInterceptor,
multi: true
},
// ...
]
Useful links:
You can find more on interceptors inside the Angular documentation.
An implementation on the authentication interceptor is found here.

Related

How to get data from protected API route

I need to get data from protected API route. I am using Next Auth with google provider.
I have seen some solutions to similar problem but all of them were using JWT token. I don't use JWT. I don't have much experience with auth so I don't know what to pass to axios request.
This is /api/auth/[...nextauth].ts
import NextAuth, { NextAuthOptions } from 'next-auth'
import GoogleProvider from 'next-auth/providers/google'
import { PrismaAdapter } from '#next-auth/prisma-adapter'
import { prisma } from '../../../server/client'
export const authOptions: NextAuthOptions = {
adapter: PrismaAdapter(prisma),
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID as string,
clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
}),
],
session: {
strategy: 'database',
},
}
export default NextAuth(authOptions)
This is how my route is protected:
const session = await getServerAuthSession({ req, res })
if (!session) {
return res.status(403).send({
error: 'You must be signed in to view the protected content on this page.',
})
}
This is get-server-auth-session.ts file
// Wrapper for unstable_getServerSession https://next-auth.js.org/configuration/nextjs
import type { GetServerSidePropsContext } from 'next'
import { unstable_getServerSession } from 'next-auth'
import { authOptions as nextAuthOptions } from '../../pages/api/auth/[...nextauth]'
// Next API route example - /pages/api/restricted.ts
export const getServerAuthSession = async (ctx: {
req: GetServerSidePropsContext['req']
res: GetServerSidePropsContext['res']
}) => {
return await unstable_getServerSession(
ctx.req,
ctx.res,
nextAuthOptions
)
}

from http to Http Client, cant get my object to become a string array [duplicate]

Following Google's official Angular 4.3.2 doc here, I was able to do a simple get request from a local json file. I wanted to practice hitting a real endpoint from JSON placeholder site, but I'm having trouble figuring out what to put in the .subscribe() operator. I made an IUser interface to capture the fields of the payload, but the line with .subscribe(data => {this.users = data}) throws the error Type 'Object' is not assignable to type 'IUser[]'. What's the proper way to handle this? Seems pretty basic but I'm a noob.
My code is below:
import { Component, OnInit } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { IUsers } from './users';
#Component({
selector: 'pm-http',
templateUrl: './http.component.html',
styleUrls: ['./http.component.css']
})
export class HttpComponent implements OnInit {
productUrl = 'https://jsonplaceholder.typicode.com/users';
users: IUsers[];
constructor(private _http: HttpClient) { }
ngOnInit(): void {
this._http.get(this.productUrl).subscribe(data => {this.users = data});
}
}
You actually have a few options here, but use generics to cast it to the type you're expecting.
// Notice the Generic of IUsers[] casting the Type for resulting "data"
this.http.get<IUsers[]>(this.productUrl).subscribe(data => ...
// or in the subscribe
.subscribe((data: IUsers[]) => ...
Also I'd recommend using async pipes in your template that auto subscribe / unsubscribe, especially if you don't need any fancy logic, and you're just mapping the value.
users: Observable<IUsers[]>; // different type now
this.users = this.http.get<IUsers[]>(this.productUrl);
// template:
*ngFor="let user of users | async"
I'm on the Angular doc team and one open todo item is to change these docs to show the "best practice" way to access Http ... which is through a service.
Here is an example:
import { Injectable } from '#angular/core';
import { HttpClient, HttpErrorResponse } from '#angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';
import { IProduct } from './product';
#Injectable()
export class ProductService {
private _productUrl = './api/products/products.json';
constructor(private _http: HttpClient) { }
getProducts(): Observable<IProduct[]> {
return this._http.get<IProduct[]>(this._productUrl)
.do(data => console.log('All: ' + JSON.stringify(data)))
.catch(this.handleError);
}
private handleError(err: HttpErrorResponse) {
// in a real world app, we may send the server to some remote logging infrastructure
// instead of just logging it to the console
let errorMessage = '';
if (err.error instanceof Error) {
// A client-side or network error occurred. Handle it accordingly.
errorMessage = `An error occurred: ${err.error.message}`;
} else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong,
errorMessage = `Server returned code: ${err.status}, error message is: ${err.message}`;
}
console.error(errorMessage);
return Observable.throw(errorMessage);
}
}
The component would then look like this:
ngOnInit(): void {
this._productService.getProducts()
.subscribe(products => this.products = products,
error => this.errorMessage = <any>error);
}

How to get both headers and JSON return with parameter in Angular 6

I am using Angular http request to get a JSON response from a wordpress rest API. When I pass a parameter I do not get a response, but without the parameter it is working fine.
Here is my code from wpgetpost.service.ts file
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders} from '#angular/common/http';
import { Observable } from 'rxjs';
import { GlobalService } from '../global/global.service';
#Injectable({
providedIn: 'root'
})
export class WpgetpostService {
constructor( private http: HttpClient, private URL: GlobalService ) { }
getPost(): Observable<any[]> {
const url = this.URL.WP_API_URL;
return this.http.get<any[]>(url, {
params: {
page: '2'
},
});
}
}
Also, how can I get the headers from this response?
as per WP documentation.
using the param within the same URL.
/wp/v2/posts?page=2

Angular 2 - How to handle HTTP responses in Component

New to Angular 2, still trying to get my head around certain things. Where I am stuck is I have login service and login component. I send a login request from the login component to the login service to post the username and password to a login API. If successful it posts the token to the localstorage. Where I am stuck is after the token is sent storage I want to return a boolean response back to the login component. Based on the boolean response it will perform execute a function in the component.
I can do everything until I get the response. I don't know how to handle a response back to the login component. Appreciate if someone could point me in the right direction. My code as follows:
LOGIN SERVICE
import { Injectable } from '#angular/core';
import { Token } from './login';
import { APIDOMAIN } from '../../../shared/api';
import { Http, Response, Headers, RequestOptions } from '#angular/http';
import { Observable } from 'rxjs/Rx';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/toPromise';
#Injectable()
export class LoginService {
url: string = APIDOMAIN;
constructor(private http: Http) { }
login(username: string, password: string) {
console.log('Login API');
let headers = new Headers();
let data = null;
headers.append("Authorization", "Basic " + btoa(username + ":" + password));
headers.append("Content-Type", "application/x-www-form-urlencoded");
this.http.post(this.url+ '/login', data, {headers: headers})
.map(res => res.json())
.subscribe(
token => { console.log(token); localStorage.setItem('id_token',token.token); },
err => { console.log(err);},
() => console.log('Request Complete')
);
}
logout(): void {
localStorage.removeItem('id_token');
}
}
LOGIN COMPONENT
import { Component, OnInit } from '#angular/core';
import { LoginService } from './shared/login.service';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs/Rx';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
username: string;
password: string;
constructor(private loginService: LoginService) { }
ngOnInit() {}
login(): void {
this.loginService.login(this.username,this.password)
// PEFORM SOME FUNCTION BASED BOOLEAN RESPONSE
}
}
Here's one solution:
export class LoginService {
status: EventEmitter<boolean> = new EventEmitter();
login(username: string, password: string) {
this.http.post(...)
.map(res => res.json())
.subscribe(token => {
console.log(token);
localStorage.setItem('id_token',token.token);
this.status.emit(true);
});
logout() {
localStorage.removeItem('id_token');
this.status.emit(false);
}
}
export class LoginComponent implements OnInit {
constructor(private loginService: LoginService) { }
ngOnInit() {
this.loginService.status.subscribe(console.info);
}
}

Using HTTP Angular

I'm trying to make a simple HTTP request using Angular, but I keep getting this error:
Property 'http' does not exist on type 'WorksService'.
import {Injectable} from 'angular2/core';
import {Http, HTTP_PROVIDERS, Response} from 'angular2/http';
import {Observable} from 'rxjs/Observable';
export class Work {
constructor(public id: number, public name: string) { }
}
#Injectable()
export class WorksService {
constructor (private http: Http) {}
private _WorksUrl = 'api/projects'; // URL to web api
getHeroes () {
return this.http.get(this._WorksUrl)
.map(res => <Work[]> res.json().data)
.catch(this.handleError);
}
private handleError (error: Response) {
// instead of just logging it to the console
console.error(error);
return Observable.throw(error.json().error || 'Server error');
}
}
I guess that you instantiate the service by your own. You need to get it through dependency injection so Angular2 will provide the http instance directly.
To do that, just add the service in providers either when bootstrapping your application or within the providers attribute of the component that uses the service (directly or indirectly).
bootstrap(AppComponent, [ HTTP_PROVIDERS, WorksService ]);
or
bootstrap(AppComponent, [ HTTP_PROVIDERS ]);
(...)
#Component({
(...)
providers: [ WorksService ]
})
export class SomeComponent {}
To inject it then, you do like this:
#Component({
(...)
})
export class SomeComponent {
constructor(private service WorksService) {
this.service.getHeroes().subscribe(
(data) => {
(...)
});
}
}

Resources