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