I am currently working on a logout module, however it gives me the following message.
my file logout.page.ts is the following, In the logout method I call a service that brings my alerts to confirm the one that is closing the session
import { Component, OnInit } from '#angular/core';
import {AlertController} from "#ionic/angular";
import {Router} from '#angular/router';
import { UserService } from '../../services/user.service'
import { AlertService} from '../../services/alert.service';
#Component({
selector: 'app-logout',
templateUrl: './logout.page.html',
styleUrls: ['./logout.page.scss'],
})
export class LogoutPage implements OnInit {
constructor(public alertController: AlertController,
public router: Router,
public userService: UserService,
public alertService: AlertService) { }
ngOnInit() {
}
logout() {
this.userService.logout().subscribe(
data => {
this.alertService.presentToast(data['message']);
},
error => {
console.log(error);
},
() => {
this.router.navigate(['']);
}
);
}
}
my file user.service.ts is the following in the logout method try to change the format where the application token is being brought, leave it in several ways until you reach this one, but when running the application again it throws at me that it cannot find the route that I am indicating
import { Injectable } from '#angular/core';
import {HttpClient, HttpHeaders } from "#angular/common/http";
import {environment} from "../../environments/environment";
import { EnvService } from './env.service';
import { NativeStorage } from '#ionic-native/native-storage/ngx';
import { tap } from 'rxjs/operators';
#Injectable({
providedIn: 'root'
})
export class UserService {
isLoggedIn = false;
token:any;
constructor(private http: HttpClient,
private storage: NativeStorage,
private env: EnvService,) { }
public getToken(username: string, password: string) {
const url = environment.api + 'oauth/token';
const params = {
grant_type: 'password',
client_id: environment.client_id,
client_secret: environment.client_secret,
'username': username,
'password': password,
scope: '*'
};
return this.http.post(url, params, {});
}
getPassport() {
const header = new HttpHeaders({
'Content_Type': 'application/json',
'Authorization': 'Bearer ' + localStorage.getItem('token')
});
}
logout() {
const headers = new HttpHeaders({
'Content_Type': 'application/json',
'Authorization': 'Bearer ' + localStorage.getItem('token')
});
return this.http.get(this.env.API_URL + 'oauth/logout', { headers: headers })
.pipe(
tap(data => {
this.storage.remove("token");
this.isLoggedIn = false;
delete this.token;
return data;
})
)
}
getTokenUrl() {
return this.storage.getItem('token').then(
data => {
this.token = data;
if(this.token != null) {
this.isLoggedIn=true;
} else {
this.isLoggedIn=false;
}
},
error => {
this.token = null;
this.isLoggedIn=false;
}
);
}
}
change my path from oauth / logout but the problem still persists, indicating that it cannot find that path
Related
I am trying to make an interceptor that handles the tokens, but I don't want to make unnecessary requests for refreshToken, that is, if I am already updating the token, I want the next request to wait until it finishes updating the token and then send the request with the new token
What i tried so far:
import { Injectable } from '#angular/core';
import { AuthService } from './auth.service';
import { HttpClient, HttpErrorResponse, HttpHandler, HttpInterceptor, HttpRequest } from '#angular/common/http';
import { environment } from 'src/environments/environment';
import { catchError, filter, map, switchMap, take } from 'rxjs/operators';
import { BehaviorSubject, Observable } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class TokenInterceptorService implements HttpInterceptor {
constructor(
private auth: AuthService,
private http: HttpClient
) { }
refreshingToken = new BehaviorSubject<boolean>(false);
intercept(req: HttpRequest<any>, next: HttpHandler) {
if (!this.auth.getToken()) {
return next.handle(req)
}
const reqClone = req.clone({
setHeaders: {
authorization: `Bearer ${this.auth.getToken()}`
}
})
return next.handle(reqClone).pipe(
catchError((err: any) => {
//if error is about http
if (err instanceof HttpErrorResponse) {
if (err.url.includes('refreshToken')) {
return next.handle(reqClone)
}
//if error is not about authorization
if (err.status !== 401) {
return next.handle(reqClone)
}
if(this.refreshingToken.value === true){
return this.refreshingToken.pipe(
filter(value => value === false),
take(1),
switchMap((value)=>{
return next.handle(reqClone)
})
)
}else{
this.refreshingToken.next(true)
return this.renewToken(req).pipe(
switchMap(reqWithNewToken => {
this.refreshingToken.next(false)
return next.handle(reqWithNewToken)
})
)
}
} else {
return next.handle(reqClone)
}
})
)
}
renewToken(req: HttpRequest<any>) {
return this.http.get(`${environment.API_URL}/refreshToken`, { withCredentials: true }).pipe(
map((res: any) => {
//update access token
this.auth.setToken(res.token)
return req.clone({
setHeaders: {
authorization: `Bearer ${res.token}`
}
})
})
)
}
}
the error:
i've created an generic request service but it keeps returning 'ZoneAwarePromise'. I've tried to use .pipe() and .subscribe() to try to retrive the content of the request, but it's not working.
requester.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class RequesterService {
constructor(private http: HttpClient) { }
request(method, url, headers, body, params) {
return this.http.request(method, url, {
body: body || {},
headers: headers || {},
params: params || {}
})
}
async get(url, params?, headers?, data?) {
return await this.request('get', url, params, headers, data)
}
async post(url, params, headers, data) {
return await this.request('post', url, params, headers, data);
}
async put(url, params, headers, data) {
return await this.request('put', url, params, headers, data);
}
async delete(url, params, headers, data) {
return await this.request('delete', url, params, headers, data);
}
}
gym-list.component.ts
import { Component, OnInit } from '#angular/core';
import { RequesterService } from 'src/app/core/Requester/requester.service';
#Component({
selector: 'app-gym-list',
templateUrl: './gym-list.component.html',
styleUrls: ['./gym-list.component.css']
})
export class GymListComponent implements OnInit {
constructor(private Requester: RequesterService) { }
ngOnInit() {
console.log(this.teste())
}
async teste() {
await this.Requester.get('https://searchs.glitch.me/proposalcontent')
}
}
import { HttpClient, HttpHeaders, HttpRequest, HttpEventType, HttpEvent, HttpResponse, HttpErrorResponse } from "#angular/common/http";
import { Observable, from } from 'rxjs';
import { IGenericHttpClient } from './igeneric-http-client';
import { Injectable } from '#angular/core';
import { HttpMethod } from '../view-models/component-type.enum'
import { IRequestOptions } from '../view-models/interface';
import { EnvironmentViewModel } from '../view-models/environment-view-model';
import { retry } from 'rxjs/operators';
#Injectable()
export class GenericHttpClient<T> implements IGenericHttpClient<T>{
constructor(private httpClient: HttpClient, private environment: EnvironmentViewModel) { }
public Post(destinationUrl: string, options?: IRequestOptions): Observable<T> {
return this.request<T>(HttpMethod.Post, destinationUrl, options);
}
public Put(destinationUrl: string, options?: IRequestOptions): Observable<T> {
return this.request<T>(HttpMethod.Put, destinationUrl, options);
}
public Get(destinationUrl: string, options?: IRequestOptions): Observable<T> {
return this.request<T>(HttpMethod.Get, destinationUrl, options);
}
public Delete(destinationUrl: string, options?: IRequestOptions): Observable<T> {
return this.request<T>(HttpMethod.Delete, destinationUrl, options);
}
private request<T>(method: string, url: string, options?: IRequestOptions): Observable<T> {
return Observable.create((observer: any) => {
this.httpClient.request<T>(new HttpRequest(method, this.environment.baseUrl + url, options)).subscribe(
(response: any) => {
const responsTye = response as HttpEvent<any>
switch (responsTye.type) {
case HttpEventType.Sent:
console.log('Request sent!');
break;
case HttpEventType.ResponseHeader:
console.log('Response header received!');
break;
case HttpEventType.DownloadProgress:
const kbLoaded = Math.round(responsTye.loaded / 1024);
console.log(`Download in progress! ${kbLoaded}Kb loaded`);
break;
case HttpEventType.Response:
observer.next(response.body);
console.log('😺 Done!', responsTye.body);
}
},
(error) => {
switch (error.status) {
case 403:
observer.complete();
break;
default:
observer.error(error);
break;
}
}
);
});
}
}
You are trying to return an non-async function with await.
request(method, url, headers, body, params) {
return this.http.request(method, url, {
body: body || {},
headers: headers || {},
params: params || {}
})
}
async get(url, params?, headers?, data?) {
return await this.request('get', url, params, headers, data)
}
You shouldn't use async-await system here.
Try this:
get(url, params?, headers?, data?) {
return this.request('get', url, params, headers, data)
}
And subscribe this method in your component.ts file.
Like this:
Requester.get.subscribe(....)
Actually, you can create an abstract base service class that can be extended in any service. This will automatically handle the error by calling another service that has a generic error handling code. If you pass customError flag the error will be returned into the component for handing custom error. This method is using RxJs Observables which can be subscribed in component.ts and catch the response and error.
import { HttpClient, HttpHeaders } from "#angular/common/http";
import { Observable } from "rxjs";
import { tap } from "rxjs/operators";
import { HttpOptionsService } from "../../services/httpoptions.service";
export abstract class ServiceBase {
constructor(
private http: HttpClient,
private httpOptionService: HttpOptionsService
) {}
httpOptions = {
headers: new HttpHeaders({
"Content-Type": "application/json"
})
};
getFormattedPostRequest(
url: string,
body: any,
customError: boolean = false): Observable<any> {
return this.pipeTapObservable(
this.http.post(url, body, this.httpOptions),
customError
);
}
getFormattedPutRequest(
url: string,
body: any,
customError: boolean = false
): Observable<any> {
return this.pipeTapObservable(
this.http.put(url, body, this.httpOptions),
customError
);
}
getFormattedGetRequest(
url: string,
customError: boolean = false
): Observable<any> {
return this.pipeTapObservable(
this.http.get(url, this.httpOptions),
customError
);
}
getFormattedDeleteRequest(
url: string,
customError: boolean = false
): Observable<any> {
return this.pipeTapObservable(
this.http.delete(url, this.httpOptions),
customError
);
}
private pipeTapObservable(
request: Observable<any>,
customError: boolean = false
): Observable<any> {
return request.pipe(
tap(
response => response,
error => {
!customError &&
this.httpOptionService.handleError(error);
return error;
}
)
);
}
}
If you have some common headers to be added you can create an interceptor to add them. You can use it in any service like below.
#Injectable()
export class MyService extends ServiceBase {
constructor(
http: HttpClient,
httpOptionService: HttpOptionsService
) {
super(http, httpOptionService);
}
}
you can use async/await but this syntax work on promise type and request method return an observable so you just need to return a promise from request method or you can do it on the return statement on the get,post,put,delete method
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class RequesterService {
constructor(private http: HttpClient) { }
request(method, url, headers, body, params) {
return this.http.request(method, url, {
body: body || {},
headers: headers || {},
params: params || {}
}).toPromise();
}
get(url, params?, headers?, data?) {
return this.request('get', url, params, headers, data);
}
post(url, params, headers, data) {
return this.request('post', url, params, headers, data);
}
put(url, params, headers, data) {
return this.request('put', url, params, headers, data);
}
delete(url, params, headers, data) {
return this.request('delete', url, params, headers, data);
}
}
component
import { Component, OnInit } from '#angular/core';
import { RequesterService } from 'src/app/core/Requester/requester.service';
#Component({
selector: 'app-gym-list',
templateUrl: './gym-list.component.html',
styleUrls: ['./gym-list.component.css']
})
export class GymListComponent implements OnInit {
constructor(private Requester: RequesterService) { }
ngOnInit() {
console.log(this.teste())
}
async teste() {
const result = await this.Requester.get('https://searchs.glitch.me/proposalcontent');
console.log(result); 👈
}
}
I'm trying to authenticate all the API calls to the backend using an HttpInterceptor in an Ionic 4 project. The token is saved through NativeStorage. The problem occurs when I make the login call. Since there is not yet an available token, NativeStorage returns an error interrupting the chain: NativeStorageError {code: 2, source: "Native", exception: null}
httpConfig.interceptor.ts
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor,
HttpResponse,
HttpErrorResponse
} from '#angular/common/http';
import { Observable, throwError, from } from 'rxjs';
import { map, catchError, switchMap } from 'rxjs/operators';
import { Injectable } from '#angular/core';
import { LoadingController } from '#ionic/angular';
import { NativeStorage } from '#ionic-native/native-storage/ngx';
const TOKEN_KEY = 'auth-token';
#Injectable()
export class HttpConfigInterceptor implements HttpInterceptor {
loaderToShow: any;
loadingPresent = false;
debug = false;
constructor(
public loadingController: LoadingController,
private storage: NativeStorage
)
{ }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return from(this.storage.getItem(TOKEN_KEY))
.pipe(
switchMap(token => {
if (token) {
request = request.clone({ headers: request.headers.set('Authorization', 'Bearer ' + token.access_token) });
}
if (!request.headers.has('Content-Type')) {
request = request.clone({ headers: request.headers.set('Content-Type', 'application/json') });
}
this.showLoader();
return next.handle(request).pipe(
map((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
console.log('event--->>>', event);
}
this.hideLoader();
return event;
}),
catchError((error: HttpErrorResponse) => {
this.hideLoader();
return throwError(error);
})
);
})
);
}
showLoader() {
console.log("show loader");
this.loaderToShow = this.loadingController.create({
message: 'Cargando datos...'
}).then((res) => {
this.loadingPresent = true;
res.present();
res.onDidDismiss().then((dis) => {
console.log('Loading dismissed!');
});
});
}
hideLoader() {
if(this.loadingPresent) {
this.loadingController.dismiss();
this.loadingPresent = false;
}
}
}
auth.service.ts
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { Platform } from '#ionic/angular';
import { tap, map } from 'rxjs/operators';
import { NativeStorage } from '#ionic-native/native-storage/ngx';
import { environment } from '../../environments/environment';
import { User } from '../models/user';
import { BehaviorSubject } from 'rxjs';
const TOKEN_KEY = 'auth-token';
#Injectable({
providedIn: 'root'
})
export class AuthService {
isLoggedIn = false;
token:any;
authenticationState = new BehaviorSubject(false);
constructor(
private http: HttpClient,
private storage: NativeStorage,
private plt: Platform
)
{
this.plt.ready().then(() => {
this.checkToken();
});
}
login(login: String, password: String) {
return this.http.post(environment.API_URL + 'auth/login',
{ login: login, password: password }
).pipe(
map(token => {
this.storage.setItem(TOKEN_KEY, token)
.then(
() => {
this.authenticationState.next(true);
},
error => console.error('Error storing item', error)
);
}),
);
}
logout() {
return this.http.get(environment.API_URL + 'auth/logout')
.pipe(
tap(data => {
return this.storage.remove(TOKEN_KEY).then(() => {
this.authenticationState.next(false);
});
})
)
}
isAuthenticated() {
return this.authenticationState.value;
}
checkToken() {
this.storage.getItem(TOKEN_KEY).then(res => {
if (res) {
this.authenticationState.next(true);
}
});
}
}
When I try to login the first time, it returns the "token not found" error by NativeStorage from the interceptor
NativeStorageError {code: 2, source: "Native", exception: null}
you try to access to 'TOKEN_KEY' item when item doesn't exist.
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return from(this.storage.getItem(TOKEN_KEY)) <-- //This causes error
or
checkToken() {
this.storage.getItem(TOKEN_KEY).then(res => { <-- //This causes error
if (res) {
this.authenticationState.next(true);
}
});
}
you must initialize default value for 'TOKEN_KEY' or add if else condition for controlling(checking) token
you can use this.storage.keys list for find your key
other error codes:
NATIVE_WRITE_FAILED = 1
ITEM_NOT_FOUND = 2
NULL_REFERENCE = 3
UNDEFINED_TYPE = 4
JSON_ERROR = 5
WRONG_PARAMETER = 6
the get function from ionic storage return the value as object { value : "token value "}
so access value property from storage result like this
request = request.clone({ headers: request.headers.set('Authorization', 'Bearer ' +
token.value) });
Interceptor
import {Injectable} from '#angular/core';
import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest} from '#angular/http';
#Injectable()
export class NoopInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req);
}
}
Errors
ERROR in C:/Users/mypc/workspace/angularapp/src/app/app.module.ts (5,9): Module '"C:/Users/mypc/workspace/angularapp/node_modules/#angular/http/http"' has no exported member 'HTTP_INTERCEPTORS'.
ERROR in C:/Users/mypc/workspace/angularapp/src/app/interceptors/401.interceptor.ts (2,9): Module '"C:/Users/mypc/workspace/angularapp/node_modules/#angular/http/http"' has no exported member 'HttpEvent'.
ERROR in C:/Users/mypc/workspace/angularapp/src/app/interceptors/401.interceptor.ts (2,20): Module '"C:/Users/mypc/workspace/angularapp/node_modules/#angular/http/http"' has no exported member 'HttpInterceptor'.
ERROR in C:/Users/mypc/workspace/angularapp/src/app/interceptors/401.interceptor.ts (2,37): Module '"C:/Users/mypc/workspace/angularapp/node_modules/#angular/http/http"' has no exported member 'HttpHandler'.
ERROR in C:/Users/mypc/workspace/angularapp/src/app/interceptors/401.interceptor.ts (2,50): Module '"C:/Users/mypc/workspace/angularapp/node_modules/#angular/http/http"' has no exported member 'HttpRequest'.
ERROR in C:/Users/mypc/workspace/angularapp/src/app/interceptors/401.interceptor.ts (6,56): Cannot find name 'Observable'.
ERROR in C:/Users/mypc/workspace/angularapp/src/app/app.module.ts (5,9): Module '"C:/Users/mypc/workspace/angularapp/node_modules/#angular/http/http"' has no exported member 'HTTP_INTERCEPTORS'.
The correct package is #angular/common/http. And, by the way, I think that it's only available as of 4.3.x versions. #angular/http will be deprecated in the future.
import { Injectable } from "#angular/core";
import { ConnectionBackend, RequestOptions, Request, RequestOptionsArgs, Response, Http, Headers } from "#angular/http";
import { Observable } from "rxjs/Rx";
import { Router } from '#angular/router';
import { environment } from "../../../environments/environment";
import {LoaderService} from '../loader/loader.service';
import {LocalStorageService} from '../_services/localstorage.service';
#Injectable()
export class InterceptedHttp extends Http {
router: Router;
loaderService:LoaderService;
constructor(backend: ConnectionBackend, defaultOptions: RequestOptions,private _router: Router,
private _loaderService:LoaderService,private _localStorageService:LocalStorageService) {
super(backend, defaultOptions);
this.router = _router;
this.loaderService=_loaderService;
}
get(url: string, options?: RequestOptionsArgs): Observable<Response> {
url = this.updateUrl(url);
return this.intercept(super.get(url, this.getRequestOptionArgs(options)));
}
post(url: string, body: string, options?: RequestOptionsArgs): Observable<Response> {
url = this.updateUrl(url);
let data=body;//url.endsWith('token')?body:JSON.stringify(body);//fr token url only
return this.intercept(super.post(url, data, this.getRequestOptionArgs(options)));
}
put(url: string, body: string, options?: RequestOptionsArgs): Observable<Response> {
url = this.updateUrl(url);
return this.intercept(super.put(url, body, this.getRequestOptionArgs(options)));
}
delete(url: string, options?: RequestOptionsArgs): Observable<Response> {
url = this.updateUrl(url);
return this.intercept(super.delete(url, this.getRequestOptionArgs(options)));
}
private updateUrl(req: string) {
this.showLoader();
return environment.origin +'/'+ req;
}
private getRequestOptionArgs(options?: RequestOptionsArgs): RequestOptionsArgs {
if (options == null) {
options = new RequestOptions();
}
if (options.headers == null) {
options.headers = new Headers();
}
options.withCredentials=true;
options.headers.append('Content-Type', 'application/json');
options.headers.append('Authorization', 'Bearer ' + this._localStorageService.getAuthToken());
//options.headers.append('Content-Type', 'application/x-www-form-urlencoded');
return options;
}
private onEnd(): void {
this.hideLoader();
}
private showLoader(): void {
this.loaderService.show();
}
private hideLoader(): void {
this.loaderService.hide();
}
intercept(observable: Observable<Response>): Observable<Response> {
//return observable.map(response => response.json())
return observable.catch((err, source) => {
if (err.status === 401) {
alert('You are not authorized to access the resource');
//setTimeout(() => this.router.navigate(['/login']), 3000);
setTimeout(() => document.location.href='/login', 3000);
return Observable.empty();
}
else if (err.status === 404) {
console.log(['http service',err]);
document.location.href='/not-found';
return Observable.empty();
}
else {
console.log(['htto errir',err]);
err.errors=this.parseErrors(err);
return Observable.throw(err);
}
}).finally(() => {
this.onEnd();
});
}
parseErrors(response): Array<string>
{
let errors:Array<string>=new Array<string>();
if (response) {
response=JSON.parse(response._body);
if (response.error_description) {
errors.push(response.error_description);
}
if (response.message) {
errors.push(response.message);
}
for (var key in response.modelState) {
for (var i = 0; i < response.modelState[key].length; i++) {
errors.push(response.modelState[key][i]);
}
}
} else {
errors.push('Server response null');
}
return errors;
}
}
You can create HttpInterceptor in Angular4 like this and HttpFactory like below:
import {XHRBackend, Http, RequestOptions} from "#angular/http";
import {Router} from "#angular/router";
import {InterceptedHttp} from "./http.interceptor";
import {LoaderService} from '../loader/loader.service';
import {LocalStorageService} from '../_services/localstorage.service';
export function httpFactory(xhrBackend: XHRBackend, requestOptions: RequestOptions,router:Router,
loaderService:LoaderService,localStorageService:LocalStorageService): Http {
return new InterceptedHttp(xhrBackend, requestOptions,router,loaderService,localStorageService);
}
I am using the localstorage in this if you don't require remove accordingly
{
provide: Http,
useFactory: httpFactory,
deps: [XHRBackend, RequestOptions,Router,LoaderService,LocalStorageService ]
},
Call like this in the app.module.ts inside the providers.
I have identity successfully authenticating a user, it passes the user back to the main site which runs
<script src="https://cdnjs.cloudflare.com/ajax/libs/oidc-client/1.2.2/oidc-client.min.js"></script>
<h1 id="waiting">Waiting...</h1>
<div id="error"></div>
<script>
new Oidc.UserManager().signinRedirectCallback().then(function (user) {
if (user == null) {
document.getElementById("waiting").style.display = "none";
document.getElementById("error").innerText = "No sign-in request pending.";
}
else {
window.location = "/";
}
})
.catch(function (er) {
document.getElementById("waiting").style.display = "none";
document.getElementById("error").innerText = er.message;
});
</script>
But when it its the home page "/" it keeps going back to unauthenticated because of
import { NgModule } from '#angular/core';
import { RouterModule } from '#angular/router';
import { UniversalModule } from 'angular2-universal';
import { AppComponent } from './components/app/app.component';
import { HomeComponent } from './components/home/home.component';
import { UnauthorizedComponent } from './components/unauthorized/unauthorized.component';
import { AuthService } from './services/shared/auth.service';
import { AuthGuardService } from './services/shared/auth-guard.service';
#NgModule({
bootstrap: [ AppComponent ],
declarations: [
AppComponent,
HomeComponent,
UnauthorizedComponent
],
imports: [
UniversalModule, // Must be first import. This automatically imports BrowserModule, HttpModule, and JsonpModule too.
RouterModule.forRoot([
{
path: '', redirectTo: 'home', pathMatch: 'full'
},
{
path: 'home',
component: HomeComponent,
canActivate: [AuthGuardService]
},
{
path: 'unauthorized',
component: UnauthorizedComponent
}
])
],
providers: [AuthService, AuthGuardService]
})
export class AppModule {
}
Where AuthGuard is
import { Injectable } from '#angular/core';
import { CanActivate, Router } from '#angular/router';
import { AuthService } from './auth.service';
#Injectable()
export class AuthGuardService implements CanActivate {
constructor(private authService: AuthService, private router: Router) {
}
canActivate() {
if (this.authService.loggedIn)
{
alert("this"); //<--- never happens
return true;
}
alert(this.authService.loggedIn); //<--- always false, happens before everything else?
this.router.navigate(['unauthorized']);
}
}
And where AuthService is
import { Injectable, EventEmitter } from '#angular/core';
import { Http, Headers, RequestOptions, Response } from '#angular/http';
import { Observable } from 'rxjs/Rx';
import { UserManager, Log, MetadataService, User } from 'oidc-client';
#Injectable()
export class AuthService {
mgr: UserManager = new UserManager(settings);
userLoadededEvent: EventEmitter<User> = new EventEmitter<User>();
currentUser: User;
loggedIn: boolean = false;
authHeaders: Headers;
constructor(private http: Http) {
this.mgr.getUser()
.then((user) => {
if (user) {
this.loggedIn = true;
alert("loggedin"); //<--- Happens all the time
this.currentUser = user;
this.userLoadededEvent.emit(user);
}
else {
this.loggedIn = false;
}
})
.catch((err) => {
this.loggedIn = false;
});
this.mgr.events.addUserUnloaded((e) => {
this.loggedIn = false;
});
}
clearState() {
this.mgr.clearStaleState().then(function () {
console.log("clearStateState success");
}).catch(function (e) {
console.log("clearStateState error", e.message);
});
}
getUser() {
this.mgr.getUser().then((user) => {
console.log("got user", user);
this.userLoadededEvent.emit(user);
}).catch(function (err) {
console.log(err);
});
}
removeUser() {
this.mgr.removeUser().then(() => {
this.userLoadededEvent.emit(null);
console.log("user removed");
}).catch(function (err) {
console.log(err);
});
}
startSigninMainWindow() {
this.mgr.signinRedirect({ data: 'some data' }).then(function () {
console.log("signinRedirect done");
}).catch(function (err) {
console.log(err);
});
}
endSigninMainWindow() {
this.mgr.signinRedirectCallback().then(function (user) {
console.log("signed in", user);
}).catch(function (err) {
console.log(err);
});
}
startSignoutMainWindow() {
this.mgr.signoutRedirect().then(function (resp) {
console.log("signed out", resp);
setTimeout(5000, () => {
console.log("testing to see if fired...");
})
}).catch(function (err) {
console.log(err);
});
};
endSignoutMainWindow() {
this.mgr.signoutRedirectCallback().then(function (resp) {
console.log("signed out", resp);
}).catch(function (err) {
console.log(err);
});
};
/**
* Example of how you can make auth request using angulars http methods.
* #param options if options are not supplied the default content type is application/json
*/
AuthGet(url: string, options?: RequestOptions): Observable<Response> {
if (options) {
options = this._setRequestOptions(options);
}
else {
options = this._setRequestOptions();
}
return this.http.get(url, options);
}
/**
* #param options if options are not supplied the default content type is application/json
*/
AuthPut(url: string, data: any, options?: RequestOptions): Observable<Response> {
let body = JSON.stringify(data);
if (options) {
options = this._setRequestOptions(options);
}
else {
options = this._setRequestOptions();
}
return this.http.put(url, body, options);
}
/**
* #param options if options are not supplied the default content type is application/json
*/
AuthDelete(url: string, options?: RequestOptions): Observable<Response> {
if (options) {
options = this._setRequestOptions(options);
}
else {
options = this._setRequestOptions();
}
return this.http.delete(url, options);
}
/**
* #param options if options are not supplied the default content type is application/json
*/
AuthPost(url: string, data: any, options?: RequestOptions): Observable<Response> {
let body = JSON.stringify(data);
if (options) {
options = this._setRequestOptions(options);
}
else {
options = this._setRequestOptions();
}
return this.http.post(url, body, options);
}
private _setAuthHeaders(user: any) {
this.authHeaders = new Headers();
this.authHeaders.append('Authorization', user.token_type + " " + user.access_token);
this.authHeaders.append('Content-Type', 'application/json');
}
private _setRequestOptions(options?: RequestOptions) {
if (options) {
options.headers.append(this.authHeaders.keys[0], this.authHeaders.values[0]);
}
else {
options = new RequestOptions({ headers: this.authHeaders, body: "" });
}
return options;
}
}
const settings: any = {
authority: 'http://localhost:5000/',
client_id: 'js',
redirect_uri: 'http://localhost:38881/auth',
post_logout_redirect_uri: 'http://localhost:38881/',
response_type: 'id_token token',
scope: 'openid profile api',
silent_redirect_uri: 'http://localhost:38881',
automaticSilentRenew: true,
//silentRequestTimeout:10000,
filterProtocolClaims: true,
loadUserInfo: true
};
The problem is that any attempt to go to home or / results in going to unauthorized even though alert("loggedin"); happens
It seems like
alert(this.authService.loggedIn); //<--- always false, happens before everything else, which is the issue why is canactive going even though the result from authservice hasn't returned
I had the same issue. The problem is that the Promise in the AuthService constructor is resolved after AuthGuardService checks whether the user is logged in. To fix this I created a new function in the AuthService:
isLoggedIn(): Observable<boolean> {
return Observable.fromPromise(this.mgr.getUser()).map<User, boolean>((user) => {
if (user) return true;
else return false;
});
}
Then, rewrite the canActivate function of the AuthGuardService to use the isLoggedIn() function instead of relying on the AuthService constructor:
canActivate() {
let self = this;
let isloggedIn = this.authService.isLoggedIn();
isloggedIn.subscribe((loggedin) => {
if(!loggedin){
self.router.navigate(['unauthorized']);
}
});
return isloggedIn;
}
This canActivate implementation returns an Observable instead of a boolean, to let it wait for the Promise in the AuthService to complete. There is also a subscription so we can decide whether to redirect to unauthorized or not.
Also see this issue: https://github.com/jmurphzyo/Angular2OidcClient/issues/21