From c55f91811a2bcaed4290e1eeff4d4e2f8ee3d953 Mon Sep 17 00:00:00 2001 From: Matthew Ross Date: Sat, 16 Jul 2016 18:03:18 -0400 Subject: [PATCH] Add and use new `authorization` endpoint The guard for routes now uses this endpoint to validate an existing JWT when the app is refreshed - instead of automatically redirecting to the login page. --- src/api/controllers/Auth.php | 22 ++++++---- src/app/shared/auth/auth.guard.ts | 11 +++-- src/app/shared/auth/auth.service.ts | 67 +++++++++++++++++++++-------- test/api/controllers/AuthTest.php | 3 -- 4 files changed, 66 insertions(+), 37 deletions(-) diff --git a/src/api/controllers/Auth.php b/src/api/controllers/Auth.php index c40be6f..3ba83ff 100644 --- a/src/api/controllers/Auth.php +++ b/src/api/controllers/Auth.php @@ -124,13 +124,9 @@ class Auth extends BaseController { $user->last_login = time(); $user->save(); - $user->security_level = $user->security_level->getValue(); - unset($user->password_hash); - unset($user->active_token); - $this->apiJson->setSuccess(); $this->apiJson->addData($jwt); - $this->apiJson->addData($user); + $this->apiJson->addData($this->sanitizeUser($user)); return $this->jsonResponse($response); } @@ -164,8 +160,6 @@ class Auth extends BaseController { public function authenticate($request, $response, $args) { if (!$request->hasHeader('Authorization')) { - $this->apiJson->addData(false); - return $this->jsonResponse($response, 400); } @@ -174,17 +168,27 @@ class Auth extends BaseController { if ($payload === null) { $this->apiJson->addAlert('error', 'Invalid access token.'); - $this->apiJson->addData(false); return $this->jsonResponse($response, 401); } + $user = new User($this->container, $payload->uid); + $this->apiJson->setSuccess(); - $this->apiJson->addData(true); + $this->apiJson->addData($jwt); + $this->apiJson->addData($this->sanitizeUser($user)); return $this->jsonResponse($response); } + private function sanitizeUser($user) { + $user->security_level = $user->security_level->getValue(); + unset($user->password_hash); + unset($user->active_token); + + return $user; + } + private static function getJwtPayload($jwt) { try { $payload = JWT::decode($jwt, self::getJwtKey(), ['HS256']); diff --git a/src/app/shared/auth/auth.guard.ts b/src/app/shared/auth/auth.guard.ts index cd7fa2e..9f47709 100644 --- a/src/app/shared/auth/auth.guard.ts +++ b/src/app/shared/auth/auth.guard.ts @@ -1,7 +1,11 @@ import { Injectable } from '@angular/core'; import { CanActivate, Router } from '@angular/router'; +import { Observable } from 'rxjs/Observable'; +import 'rxjs/add/observable/of'; + import { AuthService } from './auth.service'; +import { ApiResponse } from '../index'; @Injectable() export class AuthGuard implements CanActivate { @@ -9,12 +13,7 @@ export class AuthGuard implements CanActivate { } canActivate() { - if (this.authService.isLoggedIn) { - return true; - } - - this.router.navigate(['']); - return false; + return this.authService.authenticate(); } } diff --git a/src/app/shared/auth/auth.service.ts b/src/app/shared/auth/auth.service.ts index db3327b..72cfa2b 100644 --- a/src/app/shared/auth/auth.service.ts +++ b/src/app/shared/auth/auth.service.ts @@ -1,5 +1,6 @@ import { Injectable } from '@angular/core'; -import { Http, Response } from '@angular/http'; +import { Http, Response, Headers } from '@angular/http'; +import { Router } from '@angular/router'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/observable/of'; @@ -10,14 +11,38 @@ import { Constants } from '../constants'; @Injectable() export class AuthService { - activeUser: User; - isLoggedIn: boolean = false; + activeUser: User = null; jwtKey: string; - constructor(private http: Http, constants: Constants) { + constructor(constants: Constants, private http: Http, private router: Router) { this.jwtKey = constants.TOKEN; } + authenticate(): Observable { + let token = localStorage.getItem(this.jwtKey); + let header = new Headers({'Authorization': token}); + + return this.http.post('api/authenticate', token, { headers: header }). + map(res => { + let response: ApiResponse = res.json(); + + if (res.status === 200 && response.data.length) { + this.activeUser = response.data[1]; + } + + return true; + }). + catch((res, caught) => { + let response: ApiResponse = res.json(); + this.activeUser = null; + localStorage.removeItem(this.jwtKey); + + this.router.navigate(['']); + + return Observable.of(false); + }); + } + login(username: string, password: string, remember: boolean): Observable { // TODO Add remember flag to API @@ -29,32 +54,36 @@ export class AuthService { return this.http.post('api/login', json). map(res => { let response: ApiResponse = res.json(); - - if (res.status === 200) { - this.isLoggedIn = true; - this.activeUser = response.data[1]; - - localStorage.setItem(this.jwtKey, response.data[0]) - } + this.checkStatus(res); return response; }). catch((res, caught) => { let response: ApiResponse = res.json(); - - if (res.status === 401) { - this.activeUser = null; - this.isLoggedIn = false; - - localStorage.removeItem(this.jwtKey); - } + this.checkStatus(res); return Observable.of(response); }); } logout(): void { - this.isLoggedIn = false; + this.activeUser = null; + localStorage.removeItem(this.jwtKey); + + this.router.navigate(['']); + } + + private checkStatus(response: Response) { + if (response.status === 200) { + let apiResponse: ApiResponse = response.json(); + + this.activeUser = apiResponse.data[1]; + localStorage.setItem(this.jwtKey, apiResponse.data[0]) + } + + if (response.status === 401) { + this.logout(); + } } } diff --git a/test/api/controllers/AuthTest.php b/test/api/controllers/AuthTest.php index 7054613..b7afb53 100644 --- a/test/api/controllers/AuthTest.php +++ b/test/api/controllers/AuthTest.php @@ -147,14 +147,12 @@ class AuthTest extends PHPUnit_Framework_TestCase { $actual = $this->auth->authenticate($request, new ResponseMock(), null); $this->assertEquals('success', $actual->status); - $this->assertEquals(true, $actual->data[0]); $this->auth = new Auth(new ContainerMock()); $request->hasHeader = false; $actual = $this->auth->authenticate($request, new ResponseMock(), null); $this->assertEquals('failure', $actual->status); - $this->assertEquals(false, $actual->data[0]); $this->auth = new Auth(new ContainerMock()); $request = new RequestMock(); @@ -162,7 +160,6 @@ class AuthTest extends PHPUnit_Framework_TestCase { $actual = $this->auth->authenticate($request, new ResponseMock(), null); $this->assertEquals('failure', $actual->status); - $this->assertEquals(false, $actual->data[0]); } }