Use bearer instead of basic auth
This commit is contained in:
parent
84dae70b7f
commit
ae14a33616
10 changed files with 75 additions and 55 deletions
7
.vscode/launch.json
vendored
7
.vscode/launch.json
vendored
|
@ -4,6 +4,13 @@
|
|||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch Chrome",
|
||||
"request": "launch",
|
||||
"type": "pwa-chrome",
|
||||
"url": "http://localhost:4200",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"name": "ng serve",
|
||||
"type": "node",
|
||||
|
|
5
package-lock.json
generated
5
package-lock.json
generated
|
@ -8470,11 +8470,6 @@
|
|||
"resolved": "https://registry.npmjs.org/ng2-currency-mask/-/ng2-currency-mask-9.0.2.tgz",
|
||||
"integrity": "sha512-ydtSTEqXR32mHsCElDvmSEywmN45Dam4aWUhoYJl3eEm9T6QWgA3DzHxNiFcur2kH94gomdvlQAB490CcObGMg=="
|
||||
},
|
||||
"ngx-cookie-service": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/ngx-cookie-service/-/ngx-cookie-service-2.4.0.tgz",
|
||||
"integrity": "sha512-uR/6mCQ1t+XY5G1/irqRhoEddx1PPtmz7JHM/2nn5yQmicnj+n48x8C2PMxwaYDHKRh7QPQ9G5scR36Mmdz09A=="
|
||||
},
|
||||
"nice-try": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
"dexie": "^2.0.4",
|
||||
"ng2-charts": "^2.4.2",
|
||||
"ng2-currency-mask": "^9.0.2",
|
||||
"ngx-cookie-service": "^2.4.0",
|
||||
"rxjs": "^6.6.3",
|
||||
"tslib": "^2.0.0",
|
||||
"zone.js": "~0.10.3"
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { Component, Inject, ApplicationRef, ChangeDetectorRef } from '@angular/core';
|
||||
import { Component, Inject, ApplicationRef, ChangeDetectorRef, OnInit } from '@angular/core';
|
||||
import { Location } from '@angular/common';
|
||||
import { User } from './users/user';
|
||||
import { TWIGS_SERVICE, TwigsService } from './shared/twigs.service';
|
||||
import { CookieService } from 'ngx-cookie-service';
|
||||
import { SwUpdate } from '@angular/service-worker';
|
||||
import { first, filter, map } from 'rxjs/operators';
|
||||
import { interval, concat, BehaviorSubject } from 'rxjs';
|
||||
|
@ -14,7 +13,7 @@ import { Actionable, isActionable } from './shared/actionable';
|
|||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent {
|
||||
export class AppComponent implements OnInit {
|
||||
public title = 'Twigs';
|
||||
public backEnabled = false;
|
||||
public user = new BehaviorSubject<User>(null);
|
||||
|
@ -26,41 +25,50 @@ export class AppComponent {
|
|||
constructor(
|
||||
@Inject(TWIGS_SERVICE) private twigsService: TwigsService,
|
||||
private location: Location,
|
||||
private cookieService: CookieService,
|
||||
private router: Router,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private appRef: ApplicationRef,
|
||||
private updates: SwUpdate,
|
||||
private changeDetector: ChangeDetectorRef,
|
||||
) {
|
||||
private storage: Storage
|
||||
){}
|
||||
|
||||
ngOnInit(): void {
|
||||
const unauthenticatedRoutes = [
|
||||
'',
|
||||
'/',
|
||||
'/login',
|
||||
'/register'
|
||||
]
|
||||
if (this.cookieService.check('Authorization')) {
|
||||
this.twigsService.getProfile().subscribe(user => {
|
||||
this.user.next(user);
|
||||
if (this.router.url == '/') {
|
||||
let auth = this.storage.getItem('Authorization');
|
||||
let savedUser = JSON.parse(this.storage.getItem('user')) as User;
|
||||
if (auth && auth.length == 255) {
|
||||
if (savedUser) {
|
||||
this.user.next(savedUser);
|
||||
}
|
||||
this.twigsService.getProfile().subscribe(fetchedUser => {
|
||||
this.storage.setItem('user', JSON.stringify(fetchedUser));
|
||||
this.user.next(fetchedUser);
|
||||
if (unauthenticatedRoutes.indexOf(this.location.path()) != -1) {
|
||||
//TODO: Save last opened budget and redirect to there instead of the main list
|
||||
this.router.navigateByUrl("/budgets");
|
||||
}
|
||||
});
|
||||
} else if (unauthenticatedRoutes.indexOf(this.router.url) == -1) {
|
||||
this.router.navigateByUrl("/login");
|
||||
} else if (unauthenticatedRoutes.indexOf(this.location.path()) == -1) {
|
||||
this.router.navigateByUrl(`/login?redirect=${this.location.path()}`);
|
||||
}
|
||||
|
||||
updates.available.subscribe(
|
||||
this.updates.available.subscribe(
|
||||
event => {
|
||||
console.log('current version is', event.current);
|
||||
console.log('available version is', event.available);
|
||||
// TODO: Prompt user to click something to update
|
||||
updates.activateUpdate();
|
||||
this.updates.activateUpdate();
|
||||
},
|
||||
err => {
|
||||
|
||||
}
|
||||
);
|
||||
updates.activated.subscribe(
|
||||
this.updates.activated.subscribe(
|
||||
event => {
|
||||
console.log('old version was', event.previous);
|
||||
console.log('new version is', event.current);
|
||||
|
@ -70,10 +78,10 @@ export class AppComponent {
|
|||
}
|
||||
);
|
||||
|
||||
const appIsStable$ = appRef.isStable.pipe(first(isStable => isStable === true));
|
||||
const appIsStable$ = this.appRef.isStable.pipe(first(isStable => isStable === true));
|
||||
const everySixHours$ = interval(6 * 60 * 60 * 1000);
|
||||
const everySixHoursOnceAppIsStable$ = concat(appIsStable$, everySixHours$);
|
||||
everySixHoursOnceAppIsStable$.subscribe(() => updates.checkForUpdate());
|
||||
everySixHoursOnceAppIsStable$.subscribe(() => this.updates.checkForUpdate());
|
||||
this.user.subscribe(
|
||||
user => {
|
||||
if (user) {
|
||||
|
|
|
@ -46,7 +46,6 @@ import { TWIGS_SERVICE } from './shared/twigs.service';
|
|||
import { AuthInterceptor } from './shared/auth.interceptor';
|
||||
import { TwigsHttpService } from './shared/twigs.http.service';
|
||||
import { TwigsLocalService } from './shared/twigs.local.service';
|
||||
import { CookieService } from 'ngx-cookie-service';
|
||||
import { TransactionListComponent } from './transactions/transaction-list/transaction-list.component';
|
||||
import { EditCategoryComponent } from './categories/edit-category/edit-category.component';
|
||||
import { EditBudgetComponent } from './budgets/edit-budget/edit-budget.component';
|
||||
|
@ -114,8 +113,8 @@ export const CustomCurrencyMaskConfig: CurrencyMaskConfig = {
|
|||
{ provide: CURRENCY_MASK_CONFIG, useValue: CustomCurrencyMaskConfig },
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
|
||||
{ provide: TWIGS_SERVICE, useClass: TwigsHttpService },
|
||||
{ provide: Storage, useValue: window.localStorage },
|
||||
// { provide: TWIGS_SERVICE, useClass: TwigsLocalService },
|
||||
CookieService
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
|
|
|
@ -1,22 +1,21 @@
|
|||
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpHeaders } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CookieService } from 'ngx-cookie-service';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Injectable()
|
||||
export class AuthInterceptor implements HttpInterceptor {
|
||||
|
||||
constructor(
|
||||
private cookieService: CookieService
|
||||
private storage: Storage
|
||||
) { }
|
||||
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
if (!this.cookieService.check('Authorization')) {
|
||||
let token = this.storage.getItem('Authorization')
|
||||
if (!token) {
|
||||
return next.handle(req);
|
||||
}
|
||||
let headers = req.headers;
|
||||
headers = headers.append('Authorization', `Basic ${this.cookieService.get('Authorization')}`);
|
||||
this.cookieService.set('Authorization', this.cookieService.get('Authorization'), 14, null, null, true);
|
||||
headers = headers.append('Authorization', `Bearer ${token}`);
|
||||
return next.handle(req.clone({headers: headers}));
|
||||
}
|
||||
}
|
|
@ -1,42 +1,46 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient, HttpParams, HttpHeaders } from '@angular/common/http';
|
||||
import { BehaviorSubject, Observable, pipe, Subscriber } from 'rxjs';
|
||||
import { User, UserPermission, Permission } from '../users/user';
|
||||
import { User, UserPermission, Permission, AuthToken } from '../users/user';
|
||||
import { TwigsService } from './twigs.service';
|
||||
import { Budget } from '../budgets/budget';
|
||||
import { Category } from '../categories/category';
|
||||
import { Transaction } from '../transactions/transaction';
|
||||
import { environment } from '../../environments/environment';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { CookieService } from 'ngx-cookie-service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class TwigsHttpService implements TwigsService {
|
||||
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
private cookieService: CookieService
|
||||
) { }
|
||||
|
||||
private options = {
|
||||
withCredentials: true
|
||||
};
|
||||
|
||||
private apiUrl = environment.apiUrl;
|
||||
|
||||
private budgets: BehaviorSubject<Budget[]> = new BehaviorSubject(null);
|
||||
// Auth
|
||||
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
private storage: Storage
|
||||
) { }
|
||||
|
||||
login(email: string, password: string): Observable<User> {
|
||||
// const params = {
|
||||
// 'username': email,
|
||||
// 'password': password
|
||||
// };
|
||||
// return this.http.post<User>(this.apiUrl + '/users/login', params, this.options);
|
||||
const credentials = btoa(`${email}:${password}`)
|
||||
this.cookieService.set('Authorization', credentials, 14, null, null, true);
|
||||
return this.getProfile();
|
||||
return new Observable(emitter => {
|
||||
const params = {
|
||||
'username': email,
|
||||
'password': password
|
||||
};
|
||||
this.http.post<AuthToken>(this.apiUrl + '/users/login', params, this.options)
|
||||
.subscribe(
|
||||
auth => {
|
||||
// TODO: Use token expiration to determine cookie expiration
|
||||
this.storage.setItem('Authorization', auth.token);
|
||||
this.getProfile().subscribe(user => emitter.next(user), error => emitter.error(error));
|
||||
},
|
||||
error => emitter.error(error)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
register(username: string, email: string, password: string): Observable<User> {
|
||||
|
@ -49,8 +53,8 @@ export class TwigsHttpService implements TwigsService {
|
|||
}
|
||||
|
||||
logout(): Observable<void> {
|
||||
return Observable.create(emitter => {
|
||||
this.cookieService.delete('Authorization');
|
||||
return new Observable(emitter => {
|
||||
this.storage.removeItem('Authorization');
|
||||
emitter.next();
|
||||
emitter.complete();
|
||||
})
|
||||
|
|
|
@ -2,7 +2,7 @@ import { Component, OnInit, OnDestroy, Inject, ChangeDetectorRef } from '@angula
|
|||
import { TwigsService, TWIGS_SERVICE } from '../../shared/twigs.service';
|
||||
import { User } from '../user';
|
||||
import { AppComponent } from 'src/app/app.component';
|
||||
import { Router } from '@angular/router';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-login',
|
||||
|
@ -14,16 +14,19 @@ export class LoginComponent implements OnInit {
|
|||
public isLoading = false;
|
||||
public email: string;
|
||||
public password: string;
|
||||
private redirect: string;
|
||||
|
||||
constructor(
|
||||
private app: AppComponent,
|
||||
@Inject(TWIGS_SERVICE) private twigsService: TwigsService,
|
||||
private router: Router,
|
||||
private activatedRoute: ActivatedRoute
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.app.setTitle('Login')
|
||||
this.app.setBackEnabled(true);
|
||||
this.redirect = this.activatedRoute.snapshot.queryParamMap.get('redirect');
|
||||
}
|
||||
|
||||
login(): void {
|
||||
|
@ -31,10 +34,11 @@ export class LoginComponent implements OnInit {
|
|||
this.twigsService.login(this.email, this.password)
|
||||
.subscribe(user => {
|
||||
this.app.user.next(user);
|
||||
this.router.navigate(['/'])
|
||||
this.router.navigate([this.redirect || '/'])
|
||||
},
|
||||
error => {
|
||||
console.error(error)
|
||||
//TODO: Replace this with an in-app dialog
|
||||
alert("Login failed. Please verify you have the correct credentials");
|
||||
this.isLoading = false;
|
||||
})
|
||||
|
|
|
@ -12,6 +12,11 @@ export class User {
|
|||
}
|
||||
}
|
||||
|
||||
export class AuthToken {
|
||||
token: string;
|
||||
expiration: Date;
|
||||
}
|
||||
|
||||
export class UserPermission {
|
||||
user: User;
|
||||
permission: Permission;
|
||||
|
|
|
@ -13,4 +13,4 @@ export const environment = {
|
|||
* below file. Don't forget to comment it out in production mode
|
||||
* because it will have a performance impact when errors are thrown
|
||||
*/
|
||||
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
|
||||
import 'zone.js/dist/zone-error'; // Included with Angular CLI.
|
||||
|
|
Loading…
Reference in a new issue