24d1a4fa2345768471b3b1b18c6189e4deb10881
[oweals/peertube.git] / client / src / app / shared / auth / auth.service.ts
1 import { Injectable } from '@angular/core';
2 import { Headers, Http, Response, URLSearchParams } from '@angular/http';
3 import { Observable } from 'rxjs/Observable';
4 import { Subject } from 'rxjs/Subject';
5
6 import { AuthStatus } from './auth-status.model';
7 import { User } from './user.model';
8
9 @Injectable()
10 export class AuthService {
11   private static BASE_CLIENT_URL = '/api/v1/clients/local';
12   private static BASE_TOKEN_URL = '/api/v1/users/token';
13   private static BASE_USER_INFORMATIONS_URL = '/api/v1/users/me';
14
15   loginChangedSource: Observable<AuthStatus>;
16
17   private clientId: string;
18   private clientSecret: string;
19   private loginChanged: Subject<AuthStatus>;
20   private user: User = null;
21
22   constructor(private http: Http) {
23     this.loginChanged = new Subject<AuthStatus>();
24     this.loginChangedSource = this.loginChanged.asObservable();
25
26     // Fetch the client_id/client_secret
27     // FIXME: save in local storage?
28     this.http.get(AuthService.BASE_CLIENT_URL)
29       .map(res => res.json())
30       .catch(this.handleError)
31       .subscribe(
32         result => {
33           this.clientId = result.client_id;
34           this.clientSecret = result.client_secret;
35           console.log('Client credentials loaded.');
36         },
37         error => {
38           alert(error);
39         }
40       );
41
42     // Return null if there is nothing to load
43     this.user = User.load();
44   }
45
46   getRefreshToken() {
47     if (this.user === null) return null;
48
49     return this.user.getRefreshToken();
50   }
51
52   getRequestHeaderValue() {
53     return `${this.getTokenType()} ${this.getAccessToken()}`;
54   }
55
56   getAccessToken() {
57     if (this.user === null) return null;
58
59     return this.user.getAccessToken();
60   }
61
62   getTokenType() {
63     if (this.user === null) return null;
64
65     return this.user.getTokenType();
66   }
67
68   getUser(): User {
69     return this.user;
70   }
71
72   isLoggedIn() {
73     if (this.getAccessToken()) {
74       return true;
75     } else {
76       return false;
77     }
78   }
79
80   login(username: string, password: string) {
81     let body = new URLSearchParams();
82     body.set('client_id', this.clientId);
83     body.set('client_secret', this.clientSecret);
84     body.set('response_type', 'code');
85     body.set('grant_type', 'password');
86     body.set('scope', 'upload');
87     body.set('username', username);
88     body.set('password', password);
89
90     let headers = new Headers();
91     headers.append('Content-Type', 'application/x-www-form-urlencoded');
92
93     let options = {
94       headers: headers
95     };
96
97     return this.http.post(AuthService.BASE_TOKEN_URL, body.toString(), options)
98                     .map(res => res.json())
99                     .map(res => {
100                       res.username = username;
101                       return res;
102                     })
103                     .flatMap(res => this.fetchUserInformations(res))
104                     .map(res => this.handleLogin(res))
105                     .catch(this.handleError);
106   }
107
108   logout() {
109     // TODO: make an HTTP request to revoke the tokens
110     this.user = null;
111     User.flush();
112
113     this.setStatus(AuthStatus.LoggedOut);
114   }
115
116   refreshAccessToken() {
117     console.log('Refreshing token...');
118
119     const refreshToken = this.getRefreshToken();
120
121     let body = new URLSearchParams();
122     body.set('refresh_token', refreshToken);
123     body.set('client_id', this.clientId);
124     body.set('client_secret', this.clientSecret);
125     body.set('response_type', 'code');
126     body.set('grant_type', 'refresh_token');
127
128     let headers = new Headers();
129     headers.append('Content-Type', 'application/x-www-form-urlencoded');
130
131     let options = {
132       headers: headers
133     };
134
135     return this.http.post(AuthService.BASE_TOKEN_URL, body.toString(), options)
136                     .map(res => res.json())
137                     .map(res => this.handleRefreshToken(res))
138                     .catch(this.handleError);
139   }
140
141   private fetchUserInformations (obj: any) {
142     // Do not call authHttp here to avoid circular dependencies headaches
143
144     const headers = new Headers();
145     headers.set('Authorization', `Bearer ${obj.access_token}`);
146
147     return this.http.get(AuthService.BASE_USER_INFORMATIONS_URL, { headers })
148              .map(res => res.json())
149              .map(res => {
150                obj.id = res.id;
151                obj.role = res.role;
152                return obj;
153              }
154     );
155   }
156
157   private handleError (error: Response) {
158     console.error(error);
159     return Observable.throw(error.json() || { error: 'Server error' });
160   }
161
162   private handleLogin (obj: any) {
163     const id = obj.id;
164     const username = obj.username;
165     const role = obj.role;
166     const hash_tokens = {
167       access_token: obj.access_token,
168       token_type: obj.token_type,
169       refresh_token: obj.refresh_token
170     };
171
172     this.user = new User(id, username, role, hash_tokens);
173     this.user.save();
174
175     this.setStatus(AuthStatus.LoggedIn);
176   }
177
178   private handleRefreshToken (obj: any) {
179     this.user.refreshTokens(obj.access_token, obj.refresh_token);
180     this.user.save();
181   }
182
183   private setStatus(status: AuthStatus) {
184     this.loginChanged.next(status);
185   }
186
187 }