Created
October 19, 2016 04:55
-
-
Save usmansaleem/ea306e543a48f3ac2c4253984613591f to your computer and use it in GitHub Desktop.
Angular2 Login Service
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {Injectable} from "@angular/core"; | |
import {Http, Headers, Response} from "@angular/http"; | |
import {Observable} from "rxjs"; | |
import "rxjs/add/operator/map"; | |
/** | |
* Login Service to obtain access_token and refresh_token. | |
*/ | |
@Injectable() | |
export class LoginService { | |
public access_token: string; | |
public refresh_token: string; | |
private readonly LOGIN_URL: string = '/api/auth/login'; | |
private readonly REFRESH_URL: string = '/api/auth/token'; | |
private readonly ACCESS_STORAGE_KEY: string = 'accessStorage'; | |
private readonly REFRESH_STORAGE_KEY: string = 'refreshStorage'; | |
constructor(private http: Http) { | |
// set token if saved in local storage | |
var accessStorage = JSON.parse(localStorage.getItem(this.ACCESS_STORAGE_KEY)); | |
this.access_token = accessStorage && accessStorage.access_token; | |
var refreshStorage = JSON.parse(localStorage.getItem(this.REFRESH_STORAGE_KEY)); | |
this.refresh_token = refreshStorage && refreshStorage.refresh_token; | |
} | |
login(username, password): Observable<boolean> { | |
let headers = new Headers(); | |
headers.append('Content-Type', 'application/json'); | |
return this.http.post(this.LOGIN_URL, JSON.stringify({user: username, password: password}), {headers}) | |
.map((response: Response) => { | |
// login successful if there's a jwt token in the response | |
let recieved_access_token = response.json() && response.json().access_token; | |
let recieved_refresh_token = response.json() && response.json().refresh_token; | |
if (recieved_access_token && recieved_refresh_token) { | |
// set token property | |
this.refresh_token = recieved_refresh_token; | |
this.access_token = recieved_access_token; | |
//TODO: Is this secure enough? | |
localStorage.setItem(this.ACCESS_STORAGE_KEY, JSON.stringify({ | |
access_token: recieved_access_token | |
})); | |
localStorage.setItem(this.REFRESH_STORAGE_KEY, JSON.stringify({ | |
user: username, | |
refresh_token: recieved_refresh_token | |
})); | |
// return true to indicate successful login | |
return true; | |
} else { | |
//this is not meant to happen. It means server message protocol has been changed. | |
this.logout(); | |
return false; | |
} | |
}).catch(error => { | |
this.logout(); | |
return Observable.throw(error); | |
}); | |
} | |
/** | |
* Client side detection whether access token is expired or about to expire | |
* (i.e. considers expired a minute ahead of actual expiry). | |
* | |
* @returns {boolean} | |
*/ | |
isAccessTokenExpired(): Boolean { | |
if (this.access_token == null) { | |
return true; //consider expired if we are not authenticated yet ... | |
} | |
var base64Url = this.access_token.split('.')[1]; | |
if (base64Url) { | |
var base64 = base64Url.replace('-', '+').replace('_', '/'); | |
var parsedToken = JSON.parse(window.atob(base64)); | |
if (parsedToken) { | |
//divide by 1000 because of the way jwt dates are stored | |
//subtract 60000 milliseconds (i.e. 1 minutes) to expire token a minute ahead of actual expiry ... | |
return parsedToken.exp - 60000 <= Math.floor(Date.now() / 1000); | |
} | |
} | |
return true; //consider expired if access_token is not in valid jwt format. | |
} | |
refreshAccessToken(): Observable<Boolean> { | |
let headers = new Headers(); | |
headers.append('Content-Type', 'application/json'); | |
return this.http.post(this.REFRESH_URL, JSON.stringify({refresh_token: this.refresh_token}), {headers}) | |
.flatMap((response: Response) => { | |
// login successful if there's a jwt token in the response | |
let recieved_access_token = response.json() && response.json().access_token; | |
if (recieved_access_token) { | |
console.log("Refresh token request successful ... "); | |
// set token property | |
this.access_token = recieved_access_token; | |
// store access token in local storage | |
//TODO: Is this secure enough? | |
var accessDetails = JSON.parse(localStorage.getItem(this.ACCESS_STORAGE_KEY)); | |
accessDetails.access_token = this.access_token; | |
localStorage.setItem(this.ACCESS_STORAGE_KEY, JSON.stringify(accessDetails)); | |
return Observable.of(true); | |
} else { | |
//this is not meant to happen. | |
return Observable.of(false); | |
} | |
}).catch(requestError => { | |
if (requestError && requestError.status === 400) { | |
//we have our refresh token expired or revoked ... we should logout and rethrow this error further ... | |
this.logout(); | |
} | |
return Observable.throw(requestError); | |
}); | |
} | |
logout(): void { | |
console.log("Logging out..."); | |
// clear token remove user from local storage to log user out | |
this.access_token = null; | |
this.refresh_token = null; | |
localStorage.removeItem(this.ACCESS_STORAGE_KEY); | |
localStorage.removeItem(this.REFRESH_STORAGE_KEY); | |
} | |
isLoggedIn(): Boolean { | |
//TODO: Should we check localstorage or rely on state of this class? | |
return !(this.refresh_token == null); | |
} | |
setAuthorizationHeader(headers: Headers): void { | |
headers.delete("Authorization"); | |
headers.append("Authorization", `Bearer ` + this.access_token); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment