Browse Source

deal with refresh token in embed

Rigel Kent 3 years ago
parent
commit
4504f09f6e

+ 1 - 1
client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts

@@ -2,7 +2,7 @@ import { BytesPipe } from 'ngx-pipes'
 import { SortMeta } from 'primeng/api'
 import { Component, OnInit } from '@angular/core'
 import { ConfirmService, Notifier, RestPagination, RestTable, ServerService } from '@app/core'
-import { peertubeLocalStorage } from '@app/helpers/peertube-web-storage'
+import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
 import { RedundancyService } from '@app/shared/shared-main'
 import { I18n } from '@ngx-translate/i18n-polyfill'
 import { VideoRedundanciesTarget, VideoRedundancy } from '@shared/models'

+ 1 - 1
client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts

@@ -14,7 +14,7 @@ import { DomSanitizer } from '@angular/platform-browser'
 @Component({
   selector: 'my-video-block-list',
   templateUrl: './video-block-list.component.html',
-  styleUrls: [ '../../../shared/shared-moderation/moderation.scss', '../../../shared/shared-abuse-list/abuse-list-table.component.scss', './video-block-list.component.scss' ]
+  styleUrls: [ '../../../shared/shared-moderation/moderation.scss', './video-block-list.component.scss' ]
 })
 export class VideoBlockListComponent extends RestTable implements OnInit, AfterViewInit {
   blocklist: (VideoBlacklist & { reasonHtml?: string, embedHtml?: string })[] = []

+ 1 - 1
client/src/app/+admin/system/jobs/jobs.component.ts

@@ -1,7 +1,7 @@
 import { SortMeta } from 'primeng/api'
 import { Component, OnInit } from '@angular/core'
 import { Notifier, RestPagination, RestTable } from '@app/core'
-import { peertubeLocalStorage } from '@app/helpers/peertube-web-storage'
+import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
 import { I18n } from '@ngx-translate/i18n-polyfill'
 import { Job, JobState, JobType } from '@shared/models'
 import { JobStateClient } from '../../../../types/job-state-client.type'

+ 1 - 1
client/src/app/+videos/+video-watch/video-watch-playlist.component.ts

@@ -1,7 +1,7 @@
 import { Component, Input } from '@angular/core'
 import { Router } from '@angular/router'
 import { AuthService, ComponentPagination, LocalStorageService, Notifier, SessionStorageService, UserService } from '@app/core'
-import { peertubeLocalStorage, peertubeSessionStorage } from '@app/helpers/peertube-web-storage'
+import { peertubeLocalStorage, peertubeSessionStorage } from '@root-helpers/peertube-web-storage'
 import { VideoPlaylist, VideoPlaylistElement, VideoPlaylistService } from '@app/shared/shared-video-playlist'
 import { I18n } from '@ngx-translate/i18n-polyfill'
 import { VideoDetails, VideoPlaylistPrivacy } from '@shared/models'

+ 2 - 1
client/src/app/+videos/+video-watch/video-watch.component.ts

@@ -7,7 +7,8 @@ import { ActivatedRoute, Router } from '@angular/router'
 import { AuthService, AuthUser, ConfirmService, MarkdownService, Notifier, RestExtractor, ServerService, UserService } from '@app/core'
 import { HooksService } from '@app/core/plugins/hooks.service'
 import { RedirectService } from '@app/core/routing/redirect.service'
-import { isXPercentInViewport, peertubeLocalStorage, scrollToTop } from '@app/helpers'
+import { isXPercentInViewport, scrollToTop } from '@app/helpers'
+import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
 import { Video, VideoCaptionService, VideoDetails, VideoService } from '@app/shared/shared-main'
 import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription'
 import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist'

+ 2 - 1
client/src/app/app.component.ts

@@ -15,7 +15,8 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
 import { I18n } from '@ngx-translate/i18n-polyfill'
 import { BroadcastMessageLevel, getShortLocale, is18nPath, ServerConfig, UserRole } from '@shared/models'
 import { MenuService } from './core/menu/menu.service'
-import { peertubeLocalStorage, POP_STATE_MODAL_DISMISS } from './helpers'
+import { POP_STATE_MODAL_DISMISS } from './helpers'
+import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
 import { InstanceService } from './shared/shared-instance'
 
 @Component({

+ 2 - 61
client/src/app/core/auth/auth-user.model.ts

@@ -1,7 +1,7 @@
 import { Observable, of } from 'rxjs'
 import { map } from 'rxjs/operators'
 import { User } from '@app/core/users/user.model'
-import { peertubeLocalStorage } from '@app/helpers/peertube-web-storage'
+import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
 import {
   hasUserRight,
   MyUser as ServerMyUserModel,
@@ -12,66 +12,7 @@ import {
   UserRole,
   UserVideoQuota
 } from '@shared/models'
-
-export type TokenOptions = {
-  accessToken: string
-  refreshToken: string
-  tokenType: string
-}
-
-// Private class only used by User
-class Tokens {
-  private static KEYS = {
-    ACCESS_TOKEN: 'access_token',
-    REFRESH_TOKEN: 'refresh_token',
-    TOKEN_TYPE: 'token_type'
-  }
-
-  accessToken: string
-  refreshToken: string
-  tokenType: string
-
-  static load () {
-    const accessTokenLocalStorage = peertubeLocalStorage.getItem(this.KEYS.ACCESS_TOKEN)
-    const refreshTokenLocalStorage = peertubeLocalStorage.getItem(this.KEYS.REFRESH_TOKEN)
-    const tokenTypeLocalStorage = peertubeLocalStorage.getItem(this.KEYS.TOKEN_TYPE)
-
-    if (accessTokenLocalStorage && refreshTokenLocalStorage && tokenTypeLocalStorage) {
-      return new Tokens({
-        accessToken: accessTokenLocalStorage,
-        refreshToken: refreshTokenLocalStorage,
-        tokenType: tokenTypeLocalStorage
-      })
-    }
-
-    return null
-  }
-
-  static flush () {
-    peertubeLocalStorage.removeItem(this.KEYS.ACCESS_TOKEN)
-    peertubeLocalStorage.removeItem(this.KEYS.REFRESH_TOKEN)
-    peertubeLocalStorage.removeItem(this.KEYS.TOKEN_TYPE)
-  }
-
-  constructor (hash?: TokenOptions) {
-    if (hash) {
-      this.accessToken = hash.accessToken
-      this.refreshToken = hash.refreshToken
-
-      if (hash.tokenType === 'bearer') {
-        this.tokenType = 'Bearer'
-      } else {
-        this.tokenType = hash.tokenType
-      }
-    }
-  }
-
-  save () {
-    peertubeLocalStorage.setItem(Tokens.KEYS.ACCESS_TOKEN, this.accessToken)
-    peertubeLocalStorage.setItem(Tokens.KEYS.REFRESH_TOKEN, this.refreshToken)
-    peertubeLocalStorage.setItem(Tokens.KEYS.TOKEN_TYPE, this.tokenType)
-  }
-}
+import { TokenOptions, Tokens } from '../../../root-helpers/pure-auth-user.model'
 
 export class AuthUser extends User implements ServerMyUserModel {
   tokens: Tokens

+ 1 - 1
client/src/app/core/auth/auth.service.ts

@@ -5,7 +5,7 @@ import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'
 import { Injectable } from '@angular/core'
 import { Router } from '@angular/router'
 import { Notifier } from '@app/core/notification/notifier.service'
-import { objectToUrlEncoded, peertubeLocalStorage } from '@app/helpers'
+import { objectToUrlEncoded, peertubeLocalStorage } from '@root-helpers/index'
 import { I18n } from '@ngx-translate/i18n-polyfill'
 import { MyUser as UserServerModel, OAuthClientLocal, User, UserLogin, UserRefreshToken } from '@shared/models'
 import { environment } from '../../../environments/environment'

+ 1 - 1
client/src/app/core/rest/rest-table.ts

@@ -1,4 +1,4 @@
-import { peertubeLocalStorage } from '@app/helpers/peertube-web-storage'
+import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
 import { LazyLoadEvent, SortMeta } from 'primeng/api'
 import { RestPagination } from './rest-pagination'
 import { Subject } from 'rxjs'

+ 2 - 1
client/src/app/core/server/server.service.ts

@@ -2,7 +2,8 @@ import { Observable, of, Subject } from 'rxjs'
 import { first, map, share, shareReplay, switchMap, tap } from 'rxjs/operators'
 import { HttpClient } from '@angular/common/http'
 import { Inject, Injectable, LOCALE_ID } from '@angular/core'
-import { getDevLocale, isOnDevLocale, peertubeLocalStorage, sortBy } from '@app/helpers'
+import { getDevLocale, isOnDevLocale, sortBy } from '@app/helpers'
+import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
 import {
   getCompleteLocale,
   isDefaultLocale,

+ 2 - 15
client/src/app/core/users/user.model.ts

@@ -10,23 +10,10 @@ import {
   UserRole,
   VideoChannel
 } from '@shared/models'
+import { UserKeys } from '@root-helpers/user-keys'
 
 export class User implements UserServerModel {
-  static KEYS = {
-    ID: 'id',
-    ROLE: 'role',
-    EMAIL: 'email',
-    VIDEOS_HISTORY_ENABLED: 'videos-history-enabled',
-    USERNAME: 'username',
-    NSFW_POLICY: 'nsfw_policy',
-    WEBTORRENT_ENABLED: 'peertube-videojs-' + 'webtorrent_enabled',
-    AUTO_PLAY_VIDEO: 'auto_play_video',
-    SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO: 'auto_play_next_video',
-    AUTO_PLAY_VIDEO_PLAYLIST: 'auto_play_video_playlist',
-    THEME: 'theme',
-    LAST_ACTIVE_THEME: 'last_active_theme',
-    VIDEO_LANGUAGES: 'video_languages'
-  }
+  static KEYS = UserKeys
 
   id: number
   username: string

+ 1 - 1
client/src/app/core/wrappers/storage.service.ts

@@ -1,7 +1,7 @@
 import { Observable, Subject } from 'rxjs'
 import { filter } from 'rxjs/operators'
 import { Injectable } from '@angular/core'
-import { peertubeLocalStorage, peertubeSessionStorage } from '@app/helpers'
+import { peertubeLocalStorage, peertubeSessionStorage } from '@root-helpers/peertube-web-storage'
 
 abstract class StorageService {
   protected instance: Storage

+ 0 - 1
client/src/app/helpers/index.ts

@@ -1,6 +1,5 @@
 export * from './locales'
 export * from './constants'
 export * from './i18n-utils'
-export * from './peertube-web-storage'
 export * from './utils'
 export * from './zone'

+ 0 - 10
client/src/app/helpers/utils.ts

@@ -81,15 +81,6 @@ function immutableAssign <A, B> (target: A, source: B) {
   return Object.assign({}, target, source)
 }
 
-function objectToUrlEncoded (obj: any) {
-  const str: string[] = []
-  for (const key of Object.keys(obj)) {
-    str.push(encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]))
-  }
-
-  return str.join('&')
-}
-
 // Thanks: https://gist.github.com/ghinda/8442a57f22099bdb2e34
 function objectToFormData (obj: any, form?: FormData, namespace?: string) {
   const fd = form || new FormData()
@@ -207,7 +198,6 @@ export {
   sortBy,
   durationToString,
   lineFeedToHtml,
-  objectToUrlEncoded,
   getParameterByName,
   populateAsyncUserVideoChannels,
   getAbsoluteAPIUrl,

+ 0 - 89
client/src/app/shared/shared-abuse-list/abuse-list-table.component.scss

@@ -1,98 +1,9 @@
-@import 'variables';
-@import 'mixins';
-@import 'miniature';
-
-.table-video-link {
-  @include disable-outline;
-
-  position: relative;
-  top: 3px;
-}
-
-.table-comment-link,
-.table-account-link {
-  @include disable-outline;
-
-  color: var(--mainForegroundColor);
-
-  ::ng-deep p:last-child {
-    margin: 0;
-  }
-}
-
-.table-account-link {
-  display: flex;
-  flex-direction: column;
-}
-
 .comment-flagged-account,
 .account-flagged-handle {
   font-size: 11px;
   color: var(--greyForegroundColor);
 }
 
-.table-video {
-  display: inline-flex;
-
-  .table-video-image {
-    @include miniature-thumbnail;
-
-    $image-height: 45px;
-
-    height: $image-height;
-    width: #{(16/9) * $image-height};
-    margin-right: 0.5rem;
-    border-radius: 2px;
-    border: none;
-    background: transparent;
-    display: inline-flex;
-    justify-content: center;
-    align-items: center;
-    position: relative;
-
-    img {
-      height: 100%;
-      width: 100%;
-      border-radius: 2px;
-    }
-
-    span {
-      color: pvar(--inputPlaceholderColor);
-    }
-
-    .table-video-image-label {
-      @include static-thumbnail-overlay;
-      position: absolute;
-      border-radius: 3px;
-      font-size: 10px;
-      padding: 0 3px;
-      line-height: 1.3;
-      bottom: 2px;
-      right: 2px;
-    }
-  }
-
-  .table-video-text {
-    display: inline-flex;
-    flex-direction: column;
-    justify-content: center;
-    font-size: 90%;
-    color: pvar(--mainForegroundColor);
-    line-height: 1rem;
-
-    div .glyphicon {
-      font-size: 80%;
-      color: gray;
-      margin-left: 0.1rem;
-    }
-
-    div + div {
-      color: var(--greyForegroundColor);
-      font-size: 11px;
-    }
-  }
-}
-
 .abuse-states .glyphicon-comment {
   margin-left: 0.5rem;
 }

+ 85 - 0
client/src/app/shared/shared-moderation/moderation.scss

@@ -65,3 +65,88 @@ my-action-dropdown.show {
     display: block !important;
   }
 }
+
+.table-video-link {
+  @include disable-outline;
+
+  position: relative;
+  top: 3px;
+}
+
+.table-comment-link,
+.table-account-link {
+  @include disable-outline;
+
+  color: var(--mainForegroundColor);
+
+  ::ng-deep p:last-child {
+    margin: 0;
+  }
+}
+
+.table-account-link {
+  display: flex;
+  flex-direction: column;
+}
+
+.table-video {
+  display: inline-flex;
+
+  .table-video-image {
+    @include miniature-thumbnail;
+
+    $image-height: 45px;
+
+    height: $image-height;
+    width: #{(16/9) * $image-height};
+    margin-right: 0.5rem;
+    border-radius: 2px;
+    border: none;
+    background: transparent;
+    display: inline-flex;
+    justify-content: center;
+    align-items: center;
+    position: relative;
+
+    img {
+      height: 100%;
+      width: 100%;
+      border-radius: 2px;
+    }
+
+    span {
+      color: pvar(--inputPlaceholderColor);
+    }
+
+    .table-video-image-label {
+      @include static-thumbnail-overlay;
+      position: absolute;
+      border-radius: 3px;
+      font-size: 10px;
+      padding: 0 3px;
+      line-height: 1.3;
+      bottom: 2px;
+      right: 2px;
+    }
+  }
+
+  .table-video-text {
+    display: inline-flex;
+    flex-direction: column;
+    justify-content: center;
+    font-size: 90%;
+    color: pvar(--mainForegroundColor);
+    line-height: 1rem;
+
+    div .glyphicon {
+      font-size: 80%;
+      color: gray;
+      margin-left: 0.1rem;
+    }
+
+    div + div {
+      color: var(--greyForegroundColor);
+      font-size: 11px;
+    }
+  }
+}

+ 1 - 1
client/src/app/shared/shared-search/search.service.ts

@@ -3,7 +3,7 @@ import { catchError, map, switchMap } from 'rxjs/operators'
 import { HttpClient, HttpParams } from '@angular/common/http'
 import { Injectable } from '@angular/core'
 import { ComponentPaginationLight, RestExtractor, RestPagination, RestService } from '@app/core'
-import { peertubeLocalStorage } from '@app/helpers'
+import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
 import { Video, VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main'
 import { ResultList, SearchTargetType, Video as VideoServerModel, VideoChannel as VideoChannelServerModel } from '@shared/models'
 import { environment } from '../../../environments/environment'

+ 4 - 0
client/src/root-helpers/index.ts

@@ -0,0 +1,4 @@
+export * from './peertube-web-storage'
+export * from './utils'
+export * from './user-keys'
+export * from './pure-auth-user.model'

+ 0 - 0
client/src/app/helpers/peertube-web-storage.ts → client/src/root-helpers/peertube-web-storage.ts


+ 123 - 0
client/src/root-helpers/pure-auth-user.model.ts

@@ -0,0 +1,123 @@
+// pure version of auth-user, that doesn't import app packages
+import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
+import {
+  MyUser as ServerMyUserModel,
+  MyUserSpecialPlaylist,
+  NSFWPolicyType,
+  UserRole
+} from '@shared/models'
+import { UserKeys } from '@root-helpers/user-keys'
+
+export type TokenOptions = {
+  accessToken: string
+  refreshToken: string
+  tokenType: string
+}
+
+// Private class only used by User
+export class Tokens {
+  private static KEYS = {
+    ACCESS_TOKEN: 'access_token',
+    REFRESH_TOKEN: 'refresh_token',
+    TOKEN_TYPE: 'token_type'
+  }
+
+  accessToken: string
+  refreshToken: string
+  tokenType: string
+
+  static load () {
+    const accessTokenLocalStorage = peertubeLocalStorage.getItem(this.KEYS.ACCESS_TOKEN)
+    const refreshTokenLocalStorage = peertubeLocalStorage.getItem(this.KEYS.REFRESH_TOKEN)
+    const tokenTypeLocalStorage = peertubeLocalStorage.getItem(this.KEYS.TOKEN_TYPE)
+
+    if (accessTokenLocalStorage && refreshTokenLocalStorage && tokenTypeLocalStorage) {
+      return new Tokens({
+        accessToken: accessTokenLocalStorage,
+        refreshToken: refreshTokenLocalStorage,
+        tokenType: tokenTypeLocalStorage
+      })
+    }
+
+    return null
+  }
+
+  static flush () {
+    peertubeLocalStorage.removeItem(this.KEYS.ACCESS_TOKEN)
+    peertubeLocalStorage.removeItem(this.KEYS.REFRESH_TOKEN)
+    peertubeLocalStorage.removeItem(this.KEYS.TOKEN_TYPE)
+  }
+
+  constructor (hash?: TokenOptions) {
+    if (hash) {
+      this.accessToken = hash.accessToken
+      this.refreshToken = hash.refreshToken
+
+      if (hash.tokenType === 'bearer') {
+        this.tokenType = 'Bearer'
+      } else {
+        this.tokenType = hash.tokenType
+      }
+    }
+  }
+
+  save () {
+    peertubeLocalStorage.setItem(Tokens.KEYS.ACCESS_TOKEN, this.accessToken)
+    peertubeLocalStorage.setItem(Tokens.KEYS.REFRESH_TOKEN, this.refreshToken)
+    peertubeLocalStorage.setItem(Tokens.KEYS.TOKEN_TYPE, this.tokenType)
+  }
+}
+
+export class PureAuthUser {
+  tokens: Tokens
+  specialPlaylists: MyUserSpecialPlaylist[]
+
+  canSeeVideosLink = true
+
+  static load () {
+    const usernameLocalStorage = peertubeLocalStorage.getItem(UserKeys.USERNAME)
+    if (usernameLocalStorage) {
+      return new PureAuthUser(
+        {
+          id: parseInt(peertubeLocalStorage.getItem(UserKeys.ID), 10),
+          username: peertubeLocalStorage.getItem(UserKeys.USERNAME),
+          email: peertubeLocalStorage.getItem(UserKeys.EMAIL),
+          role: parseInt(peertubeLocalStorage.getItem(UserKeys.ROLE), 10) as UserRole,
+          nsfwPolicy: peertubeLocalStorage.getItem(UserKeys.NSFW_POLICY) as NSFWPolicyType,
+          webTorrentEnabled: peertubeLocalStorage.getItem(UserKeys.WEBTORRENT_ENABLED) === 'true',
+          autoPlayVideo: peertubeLocalStorage.getItem(UserKeys.AUTO_PLAY_VIDEO) === 'true',
+          videosHistoryEnabled: peertubeLocalStorage.getItem(UserKeys.VIDEOS_HISTORY_ENABLED) === 'true'
+        },
+        Tokens.load()
+      )
+    }
+
+    return null
+  }
+
+  constructor (userHash: Partial<ServerMyUserModel>, hashTokens: TokenOptions) {
+    this.tokens = new Tokens(hashTokens)
+    this.specialPlaylists = userHash.specialPlaylists
+  }
+
+  getAccessToken () {
+    return this.tokens.accessToken
+  }
+
+  getRefreshToken () {
+    return this.tokens.refreshToken
+  }
+
+  getTokenType () {
+    return this.tokens.tokenType
+  }
+
+  refreshTokens (accessToken: string, refreshToken: string) {
+    this.tokens.accessToken = accessToken
+    this.tokens.refreshToken = refreshToken
+  }
+
+  save () {
+    this.tokens.save()
+  }
+}

+ 15 - 0
client/src/root-helpers/user-keys.ts

@@ -0,0 +1,15 @@
+export const UserKeys = {
+  ID: 'id',
+  ROLE: 'role',
+  EMAIL: 'email',
+  VIDEOS_HISTORY_ENABLED: 'videos-history-enabled',
+  USERNAME: 'username',
+  NSFW_POLICY: 'nsfw_policy',
+  WEBTORRENT_ENABLED: 'peertube-videojs-' + 'webtorrent_enabled',
+  AUTO_PLAY_VIDEO: 'auto_play_video',
+  SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO: 'auto_play_next_video',
+  AUTO_PLAY_VIDEO_PLAYLIST: 'auto_play_video_playlist',
+  THEME: 'theme',
+  LAST_ACTIVE_THEME: 'last_active_theme',
+  VIDEO_LANGUAGES: 'video_languages'
+}

+ 12 - 0
client/src/root-helpers/utils.ts

@@ -0,0 +1,12 @@
+function objectToUrlEncoded (obj: any) {
+  const str: string[] = []
+  for (const key of Object.keys(obj)) {
+    str.push(encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]))
+  }
+
+  return str.join('&')
+}
+
+export {
+  objectToUrlEncoded
+}

+ 61 - 6
client/src/standalone/videos/embed.ts

@@ -4,7 +4,8 @@ import {
   peertubeTranslate,
   ResultList,
   ServerConfig,
-  VideoDetails
+  VideoDetails,
+  UserRefreshToken
 } from '../../../../shared'
 import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model'
 import {
@@ -17,7 +18,7 @@ import { PeerTubeEmbedApi } from './embed-api'
 import { TranslationsManager } from '../../assets/player/translations-manager'
 import videojs from 'video.js'
 import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings'
-import { AuthUser } from '@app/core/auth/auth-user.model'
+import { PureAuthUser, objectToUrlEncoded, peertubeLocalStorage } from '@root-helpers/index'
 
 type Translations = { [ id: string ]: string }
 
@@ -43,8 +44,12 @@ export class PeerTubeEmbed {
   mode: PlayerMode
   scope = 'peertube'
 
-  user: AuthUser
+  user: PureAuthUser
   headers = new Headers()
+  LOCAL_STORAGE_OAUTH_CLIENT_KEYS = {
+    CLIENT_ID: 'client_id',
+    CLIENT_SECRET: 'client_secret'
+  }
 
   static async main () {
     const videoContainerId = 'video-container'
@@ -60,12 +65,62 @@ export class PeerTubeEmbed {
     return window.location.origin + '/api/v1/videos/' + id
   }
 
+  refreshFetch (url: string, options?: Object) {
+    return fetch(url, options)
+      .then((res: Response) => {
+        if (res.status !== 401) return res
+
+        // 401 unauthorized is not catch-ed, but then-ed
+        const error = res
+
+        const refreshingTokenPromise = new Promise((resolve, reject) => {
+          const clientId: string = peertubeLocalStorage.getItem(this.LOCAL_STORAGE_OAUTH_CLIENT_KEYS.CLIENT_ID)
+          const clientSecret: string = peertubeLocalStorage.getItem(this.LOCAL_STORAGE_OAUTH_CLIENT_KEYS.CLIENT_SECRET)
+          const headers = new Headers()
+          headers.set('Content-Type', 'application/x-www-form-urlencoded')
+          const data = {
+            refresh_token: this.user.getRefreshToken(),
+            client_id: clientId,
+            client_secret: clientSecret,
+            response_type: 'code',
+            grant_type: 'refresh_token'
+          }
+
+          fetch('/api/v1/users/token', {
+            headers,
+            method: 'POST',
+            body: objectToUrlEncoded(data)
+          })
+            .then(res => res.json())
+            .then((obj: UserRefreshToken) => {
+              this.user.refreshTokens(obj.access_token, obj.refresh_token)
+              this.user.save()
+              this.headers.set('Authorization', `${this.user.getTokenType()} ${this.user.getAccessToken()}`)
+              resolve()
+            })
+            .catch((refreshTokenError: any) => {
+              reject(refreshTokenError)
+            })
+        })
+
+        return refreshingTokenPromise
+          .catch(() => {
+            // If refreshing fails, continue with original error
+            throw error
+          })
+          .then(() => fetch(url, {
+            ...options,
+            headers: this.headers
+          }))
+      })
+  }
+
   loadVideoInfo (videoId: string): Promise<Response> {
-    return fetch(this.getVideoUrl(videoId), { headers: this.headers })
+    return this.refreshFetch(this.getVideoUrl(videoId), { headers: this.headers })
   }
 
   loadVideoCaptions (videoId: string): Promise<Response> {
-    return fetch(this.getVideoUrl(videoId) + '/captions', { headers: this.headers })
+    return fetch(this.getVideoUrl(videoId) + '/captions')
   }
 
   loadConfig (): Promise<Response> {
@@ -115,7 +170,7 @@ export class PeerTubeEmbed {
 
   async init () {
     try {
-      this.user = AuthUser.load()
+      this.user = PureAuthUser.load()
       await this.initCore()
     } catch (e) {
       console.error(e)

+ 1 - 0
client/tsconfig.json

@@ -28,6 +28,7 @@
       "video.js": [ "node_modules/video.js/core" ],
       "@app/*": [ "src/app/*" ],
       "@shared/*": [ "../shared/*" ],
+      "@root-helpers/*": [ "src/root-helpers/*" ],
       "fs": [ "src/shims/noop.ts" ],
       "http": [ "src/shims/http.ts" ],
       "https": [ "src/shims/https.ts" ],

+ 1 - 1
client/webpack/webpack.video-embed.js

@@ -28,7 +28,7 @@ module.exports = function () {
 
       alias: {
         'video.js$': path.resolve('node_modules/video.js/core.js'),
-        '@app': path.resolve('src/app'),
+        '@root-helpers': path.resolve('src/root-helpers'),
         '@shared': path.resolve('../shared')
       }
     },