diff --git a/package.json b/package.json index 38b42a9ac5ccfcbcf6873b75a2b7b1673f5f6755..507482981e2dfe116aa5b8b599a71cc6f9d947ae 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "axios": "^0.19.0", + "axios-auth-refresh": "^1.0.7", "core-js": "^2.6.5", "lodash": "^4.17.11", "material-icons": "^0.3.1", diff --git a/src/sources/funkwhale.js b/src/sources/funkwhale.js index dd650911b82778af6679a3cb0a39ec380fa84519..18ce13ca7cde8aa22af992b2b319a68c32c167a0 100644 --- a/src/sources/funkwhale.js +++ b/src/sources/funkwhale.js @@ -1,8 +1,8 @@ import axios from 'axios' +import createAuthRefreshInterceptor from 'axios-auth-refresh'; import moment from 'moment' import Form from '../components/FunkwhaleForm.vue' import get from 'lodash/get' - import {getAbsoluteUrl} from "@/utils" export const SCOPES = "read:profile read:listenings" @@ -37,6 +37,51 @@ export async function createApp(ax, {baseUrl}) { return response } +function getAuthorizationHeader (account, state) { + const token = state.sources.funkwhale.appTokens[`${account.username}@${account.domain}`].access_token + return `Bearer ${token}` + +} + +export function getOauthAxios ({axiosParams, account, store}) { + // an axios instance with autorefreshing for oauth tokens + let client = axios.create(axiosParams || {}) + client.interceptors.request.use(request => { + request.headers['Authorization'] = getAuthorizationHeader(account, store.state) + return request + }) + createAuthRefreshInterceptor(client, (failedRequest) => { + console.log('Request failed, OAuth token probably expired, fetching a new oneā¦') + const refreshToken = store.state.sources.funkwhale.appTokens[`${account.username}@${account.domain}`].refresh_token + const app = store.state.sources.funkwhale.appCredentials[account.domain] + const payload = { + client_id: app.client_id, + client_secret: app.client_secret, + grant_type: "refresh_token", + refresh_token: refreshToken, + } + return client.post( + `/api/v1/oauth/token/`, + asForm(payload), + {headers: {'Content-Type': 'multipart/form-data'}} + ).then((tokenRefreshResponse) => { + store.commit('setRecursiveState', {key: "sources.funkwhale.appTokens", suffix: `${account.username}@${account.domain}`, value: tokenRefreshResponse.data}) + let newHeader = getAuthorizationHeader(account, store.state) + failedRequest.response.config.headers['Authorization'] = newHeader + return Promise.resolve() + }) + }) + return client +} + +function asForm (obj) { + let data = new FormData() + Object.entries(obj).forEach((e) => { + data.set(e[0], e[1]) + }) + return data +} + export default { id: "funkwhale", @@ -76,11 +121,11 @@ export default { code: query.code, redirect_uri: `${baseUrl}/connect/funkwhale/callback` } - let data = new FormData() - Object.entries(payload).forEach((e) => { - data.set(e[0], e[1]) - }) - const response = await ax.post(`https://${domain}/api/v1/oauth/token/`, data, {headers: {'Content-Type': 'multipart/form-data'}}) + const response = await ax.post( + `https://${domain}/api/v1/oauth/token/`, + asForm(payload), + {headers: {'Content-Type': 'multipart/form-data'}} + ) const token = response.data.access_token const client = axios.create({ baseURL: `https://${domain}`, @@ -95,9 +140,10 @@ export default { async fetch ({account, store, results, maxDays}) { results.status = `Fetching listenings...` const token = store.state.sources.funkwhale.appTokens[`${account.username}@${account.domain}`].access_token - const client = axios.create({ - baseURL: `https://${account.domain}`, - headers: {'Authorization': `Bearer ${token}`}, + const client = getOauthAxios({ + axiosParams: {baseURL: `https://${account.domain}`}, + store: store, + account: account }) const dateLimit = moment().subtract(maxDays, 'days') let url = null diff --git a/yarn.lock b/yarn.lock index 27445b249e2c5af3e8ebed775b5e15828943d789..4900c74e617d550cb945c002c5c58b61406389ac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1491,6 +1491,21 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== +axios-auth-refresh@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/axios-auth-refresh/-/axios-auth-refresh-1.0.7.tgz#b9f39131ef39190c753d386746e9aa6e7114212b" + integrity sha512-TQl1tF+MY+iDG93WpiqcnwGb1lvdJLKzi0IN/7hBkYFlpOC6xv4S+cW3QHZOBjKpFWRWqHg9fXd/PDsCvJ3uLA== + dependencies: + axios "^0.18.0" + +axios@^0.18.0: + version "0.18.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.18.1.tgz#ff3f0de2e7b5d180e757ad98000f1081b87bcea3" + integrity sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g== + dependencies: + follow-redirects "1.5.10" + is-buffer "^2.0.2" + axios@^0.19.0: version "0.19.0" resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.0.tgz#8e09bff3d9122e133f7b8101c8fbdd00ed3d2ab8"