]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - client/src/app/core/auth/auth.service.ts
Better build/dev scripts
[github/Chocobozzz/PeerTube.git] / client / src / app / core / auth / auth.service.ts
CommitLineData
230809ef 1import { Injectable } from '@angular/core';
0f3a78e7 2import { Headers, Http, Response, URLSearchParams } from '@angular/http';
14ad0c27 3import { Router } from '@angular/router';
5555f886
C
4import { Observable } from 'rxjs/Observable';
5import { Subject } from 'rxjs/Subject';
c16ce1de
C
6import 'rxjs/add/operator/map';
7import 'rxjs/add/operator/mergeMap';
c625a956 8import 'rxjs/add/observable/throw';
b1794c53 9
7ddd02c9
C
10import { NotificationsService } from 'angular2-notifications';
11
e2a2d6c8
C
12import { AuthStatus } from './auth-status.model';
13import { AuthUser } from './auth-user.model';
693b1aba 14// Do not use the barrel (dependency loop)
693b1aba 15import { RestExtractor } from '../../shared/rest';
b1794c53
C
16
17@Injectable()
18export class AuthService {
6606150c 19 private static BASE_CLIENT_URL = '/api/v1/clients/local';
bd5c83a8 20 private static BASE_TOKEN_URL = '/api/v1/users/token';
629d8d6f 21 private static BASE_USER_INFORMATIONS_URL = '/api/v1/users/me';
b1794c53 22
ccf6ed16 23 loginChangedSource: Observable<AuthStatus>;
b1794c53 24
ccf6ed16
C
25 private clientId: string;
26 private clientSecret: string;
4fd8aa32 27 private loginChanged: Subject<AuthStatus>;
7da18e44 28 private user: AuthUser = null;
ccf6ed16 29
14ad0c27
C
30 constructor(
31 private http: Http,
7ddd02c9 32 private notificationsService: NotificationsService,
14ad0c27
C
33 private restExtractor: RestExtractor,
34 private router: Router
35 ) {
ccf6ed16
C
36 this.loginChanged = new Subject<AuthStatus>();
37 this.loginChangedSource = this.loginChanged.asObservable();
23a5a916
C
38
39 // Fetch the client_id/client_secret
40 // FIXME: save in local storage?
ccf6ed16 41 this.http.get(AuthService.BASE_CLIENT_URL)
de59c48f
C
42 .map(this.restExtractor.extractDataGet)
43 .catch((res) => this.restExtractor.handleError(res))
23a5a916
C
44 .subscribe(
45 result => {
ccf6ed16
C
46 this.clientId = result.client_id;
47 this.clientSecret = result.client_secret;
23a5a916
C
48 console.log('Client credentials loaded.');
49 },
7ddd02c9 50
23a5a916 51 error => {
7ddd02c9
C
52 let errorMessage = `Cannot retrieve OAuth Client credentials: ${error.text}. \n`;
53 errorMessage += 'Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.';
54
55 // We put a bigger timeout
56 // This is an important message
57 this.notificationsService.error('Error', errorMessage, { timeOut: 7000 });
23a5a916 58 }
ad10a70b 59 );
bd5c83a8
C
60
61 // Return null if there is nothing to load
7da18e44 62 this.user = AuthUser.load();
1553e15d 63 }
b1794c53 64
bd5c83a8
C
65 getRefreshToken() {
66 if (this.user === null) return null;
67
68 return this.user.getRefreshToken();
69 }
70
e822fdae 71 getRequestHeaderValue() {
0f3a78e7 72 return `${this.getTokenType()} ${this.getAccessToken()}`;
1553e15d
C
73 }
74
0f3a78e7 75 getAccessToken() {
bd5c83a8
C
76 if (this.user === null) return null;
77
78 return this.user.getAccessToken();
1553e15d
C
79 }
80
ccf6ed16 81 getTokenType() {
bd5c83a8
C
82 if (this.user === null) return null;
83
84 return this.user.getTokenType();
1553e15d
C
85 }
86
7da18e44 87 getUser(): AuthUser {
bd5c83a8 88 return this.user;
1553e15d
C
89 }
90
7da18e44
C
91 isAdmin() {
92 if (this.user === null) return false;
93
94 return this.user.isAdmin();
95 }
96
ccf6ed16 97 isLoggedIn() {
0f3a78e7 98 if (this.getAccessToken()) {
1553e15d
C
99 return true;
100 } else {
101 return false;
102 }
103 }
104
4fd8aa32
C
105 login(username: string, password: string) {
106 let body = new URLSearchParams();
107 body.set('client_id', this.clientId);
108 body.set('client_secret', this.clientSecret);
109 body.set('response_type', 'code');
110 body.set('grant_type', 'password');
111 body.set('scope', 'upload');
112 body.set('username', username);
113 body.set('password', password);
114
115 let headers = new Headers();
116 headers.append('Content-Type', 'application/x-www-form-urlencoded');
117
118 let options = {
119 headers: headers
120 };
121
bd5c83a8 122 return this.http.post(AuthService.BASE_TOKEN_URL, body.toString(), options)
de59c48f 123 .map(this.restExtractor.extractDataGet)
bd5c83a8
C
124 .map(res => {
125 res.username = username;
126 return res;
127 })
af5e743b 128 .flatMap(res => this.mergeUserInformations(res))
bd5c83a8 129 .map(res => this.handleLogin(res))
de59c48f 130 .catch((res) => this.restExtractor.handleError(res));
4fd8aa32
C
131 }
132
133 logout() {
bd5c83a8
C
134 // TODO: make an HTTP request to revoke the tokens
135 this.user = null;
724fed29 136
7da18e44 137 AuthUser.flush();
e62f6ef7
C
138
139 this.setStatus(AuthStatus.LoggedOut);
bd5c83a8
C
140 }
141
142 refreshAccessToken() {
143 console.log('Refreshing token...');
144
145 const refreshToken = this.getRefreshToken();
146
147 let body = new URLSearchParams();
148 body.set('refresh_token', refreshToken);
149 body.set('client_id', this.clientId);
150 body.set('client_secret', this.clientSecret);
151 body.set('response_type', 'code');
152 body.set('grant_type', 'refresh_token');
153
154 let headers = new Headers();
155 headers.append('Content-Type', 'application/x-www-form-urlencoded');
156
157 let options = {
158 headers: headers
159 };
160
161 return this.http.post(AuthService.BASE_TOKEN_URL, body.toString(), options)
de59c48f 162 .map(this.restExtractor.extractDataGet)
bd5c83a8 163 .map(res => this.handleRefreshToken(res))
14ad0c27
C
164 .catch((res: Response) => {
165 // The refresh token is invalid?
166 if (res.status === 400 && res.json() && res.json().error === 'invalid_grant') {
167 console.error('Cannot refresh token -> logout...');
168 this.logout();
169 this.router.navigate(['/login']);
170
171 return Observable.throw({
c0a89c46
C
172 json: () => '',
173 text: () => 'You need to reconnect.'
14ad0c27
C
174 });
175 }
176
177 return this.restExtractor.handleError(res);
178 });
4fd8aa32
C
179 }
180
af5e743b
C
181 refreshUserInformations() {
182 const obj = {
183 access_token: this.user.getAccessToken()
184 };
185
186 this.mergeUserInformations(obj)
187 .subscribe(
188 res => {
189 this.user.displayNSFW = res.displayNSFW;
190 this.user.role = res.role;
191
192 this.user.save();
193 }
194 );
195 }
196
197 private mergeUserInformations(obj: { access_token: string }) {
629d8d6f
C
198 // Do not call authHttp here to avoid circular dependencies headaches
199
200 const headers = new Headers();
201 headers.set('Authorization', `Bearer ${obj.access_token}`);
202
203 return this.http.get(AuthService.BASE_USER_INFORMATIONS_URL, { headers })
204 .map(res => res.json())
205 .map(res => {
af5e743b
C
206 const newProperties = {
207 id: res.id,
208 role: res.role,
209 displayNSFW: res.displayNSFW
210 };
211
212 return Object.assign(obj, newProperties);
629d8d6f
C
213 }
214 );
b1794c53
C
215 }
216
bd5c83a8 217 private handleLogin (obj: any) {
629d8d6f 218 const id = obj.id;
bd5c83a8 219 const username = obj.username;
629d8d6f 220 const role = obj.role;
66dd264f 221 const email = obj.email;
af5e743b 222 const displayNSFW = obj.displayNSFW;
7da18e44 223 const hashTokens = {
bd5c83a8
C
224 access_token: obj.access_token,
225 token_type: obj.token_type,
226 refresh_token: obj.refresh_token
227 };
228
66dd264f 229 this.user = new AuthUser({ id, username, role, displayNSFW, email }, hashTokens);
bd5c83a8
C
230 this.user.save();
231
232 this.setStatus(AuthStatus.LoggedIn);
233 }
234
bd5c83a8
C
235 private handleRefreshToken (obj: any) {
236 this.user.refreshTokens(obj.access_token, obj.refresh_token);
237 this.user.save();
238 }
629d8d6f
C
239
240 private setStatus(status: AuthStatus) {
241 this.loginChanged.next(status);
242 }
243
b1794c53 244}