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