diff --git a/src/components/user_settings/user_settings.js b/src/components/user_settings/user_settings.js
index 06e72112..c7dab754 100644
--- a/src/components/user_settings/user_settings.js
+++ b/src/components/user_settings/user_settings.js
@@ -1,6 +1,10 @@
+<<<<<<< HEAD
 import { compose } from 'vue-compose'
 import unescape from 'lodash/unescape'
 import get from 'lodash/get'
+=======
+import { unescape, truncate } from 'lodash'
+>>>>>>> Add OAuth Tokens management to settings
 
 import TabSwitcher from '../tab_switcher/tab_switcher.js'
 import ImageCropper from '../image_cropper/image_cropper.vue'
@@ -62,6 +66,9 @@ const UserSettings = {
       activeTab: 'profile'
     }
   },
+  created () {
+    this.$store.dispatch('fetchTokens')
+  },
   components: {
     StyleSwitcher,
     TabSwitcher,
@@ -87,8 +94,20 @@ const UserSettings = {
         direct: { selected: this.newDefaultScope === 'direct' }
       }
     },
+<<<<<<< HEAD
     currentSaveStateNotice () {
       return this.$store.state.interface.settings.currentSaveStateNotice
+=======
+    oauthTokens () {
+      return this.$store.state.oauthTokens.tokens.map(oauthToken => {
+        return {
+          id: oauthToken.id,
+          token: truncate(oauthToken.token, { length: 15 }),
+          refreshToken: truncate(oauthToken.refresh_token, { length: 15 }),
+          validUntil: new Date(oauthToken.valid_until).toLocaleDateString()
+        }
+      })
+>>>>>>> Add OAuth Tokens management to settings
     }
   },
   methods: {
@@ -308,6 +327,11 @@ const UserSettings = {
     logout () {
       this.$store.dispatch('logout')
       this.$router.replace('/')
+    },
+    revokeToken (id) {
+      if(confirm('Are you sure?')) {
+        this.$store.dispatch('revokeToken', id)
+      }
     }
   }
 }
diff --git a/src/components/user_settings/user_settings.vue b/src/components/user_settings/user_settings.vue
index 983cbda0..ac75ad86 100644
--- a/src/components/user_settings/user_settings.vue
+++ b/src/components/user_settings/user_settings.vue
@@ -121,6 +121,30 @@
             <p v-if="changePasswordError">{{changePasswordError}}</p>
           </div>
 
+          <div class="setting-item">
+            <h2>{{$t('settings.oauth_tokens')}}</h2>
+            <table class="oauth-tokens">
+              <thead>
+                <tr>
+                  <th>Token</th>
+                  <th>Refresh Token</th>
+                  <th>Valid Until</th>
+                  <th></th>
+                </tr>
+              </thead>
+              <tbody>
+                <tr v-for="oauthToken in oauthTokens" :key="oauthToken.id">
+                  <td>{{oauthToken.token}}</td>
+                  <td>{{oauthToken.refreshToken}}</td>
+                  <td>{{oauthToken.validUntil}}</td>
+                  <td class="actions">
+                    <button class="btn btn-default" @click="revokeToken(oauthToken.id)">Revoke</button>
+                  </td>
+                </tr>
+              </tbody>
+            </table>
+          </div>
+
           <div class="setting-item">
             <h2>{{$t('settings.delete_account')}}</h2>
             <p v-if="!deletingAccount">{{$t('settings.delete_account_description')}}</p>
@@ -213,5 +237,17 @@
     border-radius: $fallback--avatarRadius;
     border-radius: var(--avatarRadius, $fallback--avatarRadius);
   }
+
+  .oauth-tokens {
+    width: 100%;
+
+    th {
+      text-align: left;
+    }
+
+    .actions {
+      text-align: right;
+    }
+  }
 }
 </style>
diff --git a/src/i18n/ar.json b/src/i18n/ar.json
index ac7d0f1a..242dab78 100644
--- a/src/i18n/ar.json
+++ b/src/i18n/ar.json
@@ -134,6 +134,11 @@
         "notification_visibility_mentions": "الإشارات",
         "notification_visibility_repeats": "",
         "nsfw_clickthrough": "",
+        "oauth_tokens": "رموز OAuth",
+        "token": "رمز",
+        "refresh_token": "رمز التحديث",
+        "valid_until": "صالح حتى",
+        "revoke_token": "سحب",
         "panelRadius": "",
         "pause_on_unfocused": "",
         "presets": "النماذج",
diff --git a/src/i18n/ca.json b/src/i18n/ca.json
index fa517e22..d2f285df 100644
--- a/src/i18n/ca.json
+++ b/src/i18n/ca.json
@@ -132,6 +132,11 @@
     "notification_visibility_repeats": "Republica una entrada meva",
     "no_rich_text_description": "Neteja el formatat de text de totes les entrades",
     "nsfw_clickthrough": "Amaga el contingut NSFW darrer d'una imatge clicable",
+    "oauth_tokens": "Llistats OAuth",
+    "token": "Token",
+    "refresh_token": "Actualitza el token",
+    "valid_until": "Vàlid fins",
+    "revoke_token": "Revocar",
     "panelRadius": "Panells",
     "pause_on_unfocused": "Pausa la reproducció en continu quan la pestanya perdi el focus",
     "presets": "Temes",
diff --git a/src/i18n/de.json b/src/i18n/de.json
index d0bfba38..07d44348 100644
--- a/src/i18n/de.json
+++ b/src/i18n/de.json
@@ -159,6 +159,11 @@
     "hide_follows_description": "Zeige nicht, wem ich folge",
     "hide_followers_description": "Zeige nicht, wer mir folgt",
     "nsfw_clickthrough": "Aktiviere ausblendbares Overlay für Anhänge, die als NSFW markiert sind",
+    "oauth_tokens": "OAuth-Token",
+    "token": "Zeichen",
+    "refresh_token": "Token aktualisieren",
+    "valid_until": "Gültig bis",
+    "revoke_token": "Widerrufen",
     "panelRadius": "Panel",
     "pause_on_unfocused": "Streaming pausieren, wenn das Tab nicht fokussiert ist",
     "presets": "Voreinstellungen",
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 8e837d2f..0aeb70ab 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -189,6 +189,11 @@
     "show_admin_badge": "Show Admin badge in my profile",
     "show_moderator_badge": "Show Moderator badge in my profile",
     "nsfw_clickthrough": "Enable clickthrough NSFW attachment hiding",
+    "oauth_tokens": "OAuth tokens",
+    "token": "Token",
+    "refresh_token": "Refresh Token",
+    "valid_until": "Valid Until",
+    "revoke_token": "Revoke",
     "panelRadius": "Panels",
     "pause_on_unfocused": "Pause streaming when tab is not focused",
     "presets": "Presets",
diff --git a/src/i18n/es.json b/src/i18n/es.json
index d14e7a31..167e8c42 100644
--- a/src/i18n/es.json
+++ b/src/i18n/es.json
@@ -171,6 +171,11 @@
     "show_admin_badge": "Mostrar la placa de administrador en mi perfil",
     "show_moderator_badge": "Mostrar la placa de moderador en mi perfil",
     "nsfw_clickthrough": "Activar el clic para ocultar los adjuntos NSFW",
+    "oauth_tokens": "Tokens de OAuth",
+    "token": "Token",
+    "refresh_token": "Actualizar el token",
+    "valid_until": "Válido hasta",
+    "revoke_token": "Revocar",
     "panelRadius": "Paneles",
     "pause_on_unfocused": "Parar la transmisión cuando no estés en foco.",
     "presets": "Por defecto",
diff --git a/src/i18n/fi.json b/src/i18n/fi.json
index a8259ca8..c7a25fe1 100644
--- a/src/i18n/fi.json
+++ b/src/i18n/fi.json
@@ -166,6 +166,11 @@
     "no_rich_text_description": "Älä näytä tekstin muotoilua.",
     "hide_network_description": "Älä näytä seurauksiani tai seuraajiani",
     "nsfw_clickthrough": "Piilota NSFW liitteet klikkauksen taakse",
+    "oauth_tokens": "OAuth-merkit",
+    "token": "Token",
+    "refresh_token": "Päivitä token",
+    "valid_until": "Voimassa asti",
+    "revoke_token": "Peruuttaa",
     "panelRadius": "Ruudut",
     "pause_on_unfocused": "Pysäytä automaattinen viestien näyttö välilehden ollessa pois fokuksesta",
     "presets": "Valmiit teemat",
diff --git a/src/i18n/fr.json b/src/i18n/fr.json
index 129b7d7c..1209556a 100644
--- a/src/i18n/fr.json
+++ b/src/i18n/fr.json
@@ -137,6 +137,11 @@
     "notification_visibility_mentions": "Mentionnés",
     "notification_visibility_repeats": "Partages",
     "nsfw_clickthrough": "Masquer les images marquées comme contenu adulte ou sensible",
+    "oauth_tokens": "Jetons OAuth",
+    "token": "Jeton",
+    "refresh_token": "Refresh Token",
+    "valid_until": "Valable jusque",
+    "revoke_token": "Révoquer",
     "panelRadius": "Fenêtres",
     "pause_on_unfocused": "Suspendre le streaming lorsque l'onglet n'est pas centré",
     "presets": "Thèmes prédéfinis",
diff --git a/src/i18n/ga.json b/src/i18n/ga.json
index 64461202..5be9297a 100644
--- a/src/i18n/ga.json
+++ b/src/i18n/ga.json
@@ -134,6 +134,11 @@
     "notification_visibility_repeats": "Atphostáil",
     "no_rich_text_description": "Bain formáidiú téacs saibhir ó gach post",
     "nsfw_clickthrough": "Cumasaigh an ceangaltán NSFW cliceáil ar an gcnaipe",
+    "oauth_tokens": "Tocanna OAuth",
+    "token": "Token",
+    "refresh_token": "Athnuachan Comórtas",
+    "valid_until": "Bailí Go dtí",
+    "revoke_token": "Athghairm",
     "panelRadius": "Painéil",
     "pause_on_unfocused": "Sruthú ar sos nuair a bhíonn an fócas caillte",
     "presets": "Réamhshocruithe",
diff --git a/src/i18n/he.json b/src/i18n/he.json
index 99ae9551..213e6170 100644
--- a/src/i18n/he.json
+++ b/src/i18n/he.json
@@ -129,6 +129,11 @@
     "notification_visibility_mentions": "אזכורים",
     "notification_visibility_repeats": "חזרות",
     "nsfw_clickthrough": "החל החבאת צירופים לא בטוחים לצפיה בעת עבודה בעזרת לחיצת עכבר",
+    "oauth_tokens": "אסימוני OAuth",
+    "token": "אסימון",
+    "refresh_token": "רענון האסימון",
+    "valid_until": "בתוקף עד",
+    "revoke_token": "בטל",
     "panelRadius": "פאנלים",
     "pause_on_unfocused": "השהה זרימת הודעות כשהחלון לא בפוקוס",
     "presets": "ערכים קבועים מראש",
diff --git a/src/i18n/it.json b/src/i18n/it.json
index 8f69e7c1..385d21aa 100644
--- a/src/i18n/it.json
+++ b/src/i18n/it.json
@@ -93,6 +93,11 @@
     "notification_visibility_mentions": "Menzioni",
     "notification_visibility_repeats": "Condivisioni",
     "no_rich_text_description": "Togli la formattazione del testo da tutti i post",
+    "oauth_tokens": "Token OAuth",
+    "token": "Token",
+    "refresh_token": "Aggiorna token",
+    "valid_until": "Valido fino a",
+    "revoke_token": "Revocare",
     "panelRadius": "Pannelli",
     "pause_on_unfocused": "Metti in pausa l'aggiornamento continuo quando la scheda non è in primo piano",
     "presets": "Valori predefiniti",
diff --git a/src/i18n/ja.json b/src/i18n/ja.json
index 7849aa20..b51fa7fd 100644
--- a/src/i18n/ja.json
+++ b/src/i18n/ja.json
@@ -171,6 +171,11 @@
     "show_admin_badge": "アドミンのしるしをみる",
     "show_moderator_badge": "モデレーターのしるしをみる",
     "nsfw_clickthrough": "NSFWなファイルをかくす",
+    "oauth_tokens": "OAuthトークン",
+    "token": "トークン",
+    "refresh_token": "トークンを更新",
+    "valid_until": "まで有効",
+    "revoke_token": "取り消す",
     "panelRadius": "パネル",
     "pause_on_unfocused": "タブにフォーカスがないときストリーミングをとめる",
     "presets": "プリセット",
diff --git a/src/i18n/ko.json b/src/i18n/ko.json
index f9e4dfa3..336e464f 100644
--- a/src/i18n/ko.json
+++ b/src/i18n/ko.json
@@ -159,6 +159,11 @@
     "hide_follows_description": "내가 팔로우하는 사람을 표시하지 않음",
     "hide_followers_description": "나를 따르는 사람을 보여주지 마라.",
     "nsfw_clickthrough": "NSFW 이미지 \"클릭해서 보이기\"를 활성화",
+    "oauth_tokens": "OAuth 토큰",
+    "token": "토큰",
+    "refresh_token": "토큰 새로 고침",
+    "valid_until": "까지 유효하다",
+    "revoke_token": "취소",
     "panelRadius": "패널",
     "pause_on_unfocused": "탭이 활성 상태가 아닐 때 스트리밍 멈추기",
     "presets": "프리셋",
diff --git a/src/i18n/nb.json b/src/i18n/nb.json
index 0f4dca58..39e054f7 100644
--- a/src/i18n/nb.json
+++ b/src/i18n/nb.json
@@ -132,6 +132,11 @@
     "notification_visibility_repeats": "Gjentakelser",
     "no_rich_text_description": "Fjern all formatering fra statuser",
     "nsfw_clickthrough": "Krev trykk for å vise statuser som kan være upassende",
+    "oauth_tokens": "OAuth Tokens",
+    "token": "Pollett",
+    "refresh_token": "Refresh Token",
+    "valid_until": "Gyldig til",
+    "revoke_token": "Tilbakekall",
     "panelRadius": "Panel",
     "pause_on_unfocused": "Stopp henting av poster når vinduet ikke er i fokus",
     "presets": "Forhåndsdefinerte tema",
diff --git a/src/i18n/nl.json b/src/i18n/nl.json
index bb388a90..799e22b9 100644
--- a/src/i18n/nl.json
+++ b/src/i18n/nl.json
@@ -159,6 +159,11 @@
     "no_rich_text_description": "Strip rich text formattering van alle posts",
     "hide_network_description": "Toon niet wie mij volgt en wie ik volg.",
     "nsfw_clickthrough": "Schakel doorklikbaar verbergen van NSFW bijlages in",
+    "oauth_tokens": "OAuth-tokens",
+    "token": "Token",
+    "refresh_token": "Token vernieuwen",
+    "valid_until": "Geldig tot",
+    "revoke_token": "Intrekken",
     "panelRadius": "Panelen",
     "pause_on_unfocused": "Pauzeer streamen wanneer de tab niet gefocused is",
     "presets": "Presets",
diff --git a/src/i18n/oc.json b/src/i18n/oc.json
index 2ce666c6..db66bb98 100644
--- a/src/i18n/oc.json
+++ b/src/i18n/oc.json
@@ -142,6 +142,7 @@
     "notification_visibility_mentions": "Mencions",
     "notification_visibility_repeats": "Repeticions",
     "no_rich_text_description": "Netejar lo format tèxte de totas las publicacions",
+    "oauth_tokens": "Llistats OAuth",
     "pause_on_unfocused": "Pausar la difusion quand l’onglet es pas seleccionat",
     "profile_tab": "Perfil",
     "replies_in_timeline": "Responsas del flux",
diff --git a/src/i18n/pl.json b/src/i18n/pl.json
index a3952d4f..2e1d7488 100644
--- a/src/i18n/pl.json
+++ b/src/i18n/pl.json
@@ -86,6 +86,11 @@
     "name_bio": "Imię i bio",
     "new_password": "Nowe hasło",
     "nsfw_clickthrough": "Włącz domyślne ukrywanie załączników o treści nieprzyzwoitej (NSFW)",
+    "oauth_tokens": "Tokeny OAuth",
+    "token": "Token",
+    "refresh_token": "Odśwież token",
+    "valid_until": "Ważne do",
+    "revoke_token": "Odwołać",
     "panelRadius": "Panele",
     "presets": "Gotowe motywy",
     "profile_background": "Tło profilu",
diff --git a/src/i18n/ru.json b/src/i18n/ru.json
index 4b0bd4b4..6799cc96 100644
--- a/src/i18n/ru.json
+++ b/src/i18n/ru.json
@@ -132,6 +132,11 @@
     "show_admin_badge": "Показывать значок администратора в моем профиле",
     "show_moderator_badge": "Показывать значок модератора в моем профиле",
     "nsfw_clickthrough": "Включить скрытие NSFW вложений",
+    "oauth_tokens": "OAuth токены",
+    "token": "Токен",
+    "refresh_token": "Рефреш токен",
+    "valid_until": "Годен до",
+    "revoke_token": "Удалить",
     "panelRadius": "Панели",
     "pause_on_unfocused": "Приостановить загрузку когда вкладка не в фокусе",
     "presets": "Пресеты",
diff --git a/src/i18n/zh.json b/src/i18n/zh.json
index 7ad23c57..089a98e2 100644
--- a/src/i18n/zh.json
+++ b/src/i18n/zh.json
@@ -134,6 +134,11 @@
     "notification_visibility_repeats": "转发",
     "no_rich_text_description": "不显示富文本格式",
     "nsfw_clickthrough": "将不和谐附件隐藏,点击才能打开",
+    "oauth_tokens": "OAuth令牌",
+    "token": "代币",
+    "refresh_token": "刷新令牌",
+    "valid_until": "有效期至",
+    "revoke_token": "撤消",
     "panelRadius": "面板",
     "pause_on_unfocused": "在离开页面时暂停时间线推送",
     "presets": "预置",
diff --git a/src/main.js b/src/main.js
index adeb0550..2844194e 100644
--- a/src/main.js
+++ b/src/main.js
@@ -11,6 +11,7 @@ import configModule from './modules/config.js'
 import chatModule from './modules/chat.js'
 import oauthModule from './modules/oauth.js'
 import mediaViewerModule from './modules/media_viewer.js'
+import oauthTokensModule from './modules/oauth_tokens.js'
 
 import VueTimeago from 'vue-timeago'
 import VueI18n from 'vue-i18n'
@@ -64,7 +65,8 @@ createPersistedState(persistedStateOptions).then((persistedState) => {
       config: configModule,
       chat: chatModule,
       oauth: oauthModule,
-      mediaViewer: mediaViewerModule
+      mediaViewer: mediaViewerModule,
+      oauthTokens: oauthTokensModule
     },
     plugins: [persistedState, pushNotifications],
     strict: false // Socket modifies itself, let's ignore this for now.
diff --git a/src/modules/oauth_tokens.js b/src/modules/oauth_tokens.js
new file mode 100644
index 00000000..00ac1431
--- /dev/null
+++ b/src/modules/oauth_tokens.js
@@ -0,0 +1,26 @@
+const oauthTokens = {
+  state: {
+    tokens: []
+  },
+  actions: {
+    fetchTokens ({rootState, commit}) {
+      rootState.api.backendInteractor.fetchOAuthTokens().then((tokens) => {
+        commit('swapTokens', tokens)
+      })
+    },
+    revokeToken ({rootState, commit, state}, id) {
+      rootState.api.backendInteractor.revokeOAuthToken(id).then((response) => {
+        if (response.status === 201) {
+          commit('swapTokens', state.tokens.filter(token => token.id !== id))
+        }
+      })
+    }
+  },
+  mutations: {
+    swapTokens (state, tokens) {
+      state.tokens = tokens
+    }
+  }
+}
+
+export default oauthTokens
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js
index 3d2e8823..7b04343d 100644
--- a/src/services/api/api.service.js
+++ b/src/services/api/api.service.js
@@ -531,6 +531,23 @@ const fetchBlocks = ({page, credentials}) => {
   })
 }
 
+const fetchOAuthTokens = ({credentials}) => {
+  const url = '/api/oauth_tokens.json'
+
+  return fetch(url, {
+    headers: authHeaders(credentials)
+  }).then((data) => data.json())
+}
+
+const revokeOAuthToken = ({id, credentials}) => {
+  const url = `/api/oauth_tokens/${id}`
+
+  return fetch(url, {
+    headers: authHeaders(credentials),
+    method: 'DELETE'
+  })
+}
+
 const suggestions = ({credentials}) => {
   return fetch(SUGGESTIONS_URL, {
     headers: authHeaders(credentials)
@@ -573,6 +590,8 @@ const apiService = {
   setUserMute,
   fetchMutes,
   fetchBlocks,
+  fetchOAuthTokens,
+  revokeOAuthToken,
   register,
   getCaptcha,
   updateAvatar,
diff --git a/src/services/backend_interactor_service/backend_interactor_service.js b/src/services/backend_interactor_service/backend_interactor_service.js
index 43c914d9..2278cd45 100644
--- a/src/services/backend_interactor_service/backend_interactor_service.js
+++ b/src/services/backend_interactor_service/backend_interactor_service.js
@@ -65,6 +65,8 @@ const backendInteractorService = (credentials) => {
   const fetchMutes = () => apiService.fetchMutes({credentials})
   const fetchBlocks = (params) => apiService.fetchBlocks({credentials, ...params})
   const fetchFollowRequests = () => apiService.fetchFollowRequests({credentials})
+  const fetchOAuthTokens = () => apiService.fetchOAuthTokens({credentials})
+  const revokeOAuthToken = (id) => apiService.revokeOAuthToken({id, credentials})
 
   const getCaptcha = () => apiService.getCaptcha()
   const register = (params) => apiService.register(params)
@@ -96,6 +98,8 @@ const backendInteractorService = (credentials) => {
     setUserMute,
     fetchMutes,
     fetchBlocks,
+    fetchOAuthTokens,
+    revokeOAuthToken,
     register,
     getCaptcha,
     updateAvatar,
diff --git a/test/unit/specs/modules/users.spec.js b/test/unit/specs/modules/users.spec.js
index 4d49ee24..96ae845b 100644
--- a/test/unit/specs/modules/users.spec.js
+++ b/test/unit/specs/modules/users.spec.js
@@ -32,6 +32,16 @@ describe('The users module', () => {
 
       expect(user.muted).to.eql(false)
     })
+
+    it('sets oauth tokens', () => {
+      const state = cloneDeep(defaultState)
+      const tokens = [{ id: 1, token: 'bar' }]
+
+      mutations.addOAuthTokens(state, tokens)
+
+      expect(state.oauthTokens).to.have.length(1)
+      expect(state.oauthTokens).to.eql(tokens)
+    })
   })
 
   describe('getUserByName', () => {