Prechádzať zdrojové kódy

Onedrive refresh tokens (#4655)

* handle google drive refresh token revoked

* implement onedrive refresh tokens #2721
Mikael Finstad 1 rok pred
rodič
commit
682ae7fe43

+ 1 - 1
packages/@uppy/companion/src/companion.js

@@ -136,7 +136,7 @@ module.exports.app = (optionsArg = {}) => {
 
   app.get('/:providerName/thumbnail/:id', middlewares.hasSessionAndProvider, middlewares.hasOAuthProvider, middlewares.cookieAuthToken, middlewares.verifyToken, controllers.thumbnail)
 
-  app.param('providerName', providerManager.getProviderMiddleware(providers))
+  app.param('providerName', providerManager.getProviderMiddleware(providers, grantConfig))
 
   if (app.get('env') !== 'test') {
     jobs.startCleanUpJob(options.filePath)

+ 2 - 1
packages/@uppy/companion/src/server/controllers/refresh-token.js

@@ -9,6 +9,7 @@ async function refreshToken (req, res, next) {
   const { providerName } = req.params
 
   const { key: clientId, secret: clientSecret } = req.companion.options.providerOptions[providerName]
+  const { redirect_uri: redirectUri } = req.companion.providerGrantConfig
 
   const providerTokens = req.companion.allProvidersTokens[providerName]
 
@@ -20,7 +21,7 @@ async function refreshToken (req, res, next) {
 
   try {
     const data = await req.companion.provider.refreshToken({
-      clientId, clientSecret, refreshToken: providerTokens.refreshToken,
+      redirectUri, clientId, clientSecret, refreshToken: providerTokens.refreshToken,
     })
 
     const newAllProvidersTokens = {

+ 5 - 2
packages/@uppy/companion/src/server/provider/drive/index.js

@@ -171,7 +171,7 @@ class Drive extends Provider {
 
   async refreshToken ({ clientId, clientSecret, refreshToken }) {
     return this.#withErrorHandling('provider.drive.token.refresh.error', async () => {
-      const { access_token: accessToken } = await getOauthClient().post('token', { form: { refresh_token: refreshToken, grant_type: 'refresh_token', client_id: clientId, client_secret: clientSecret } }).json()
+      const { access_token: accessToken } = await getOauthClient().post('token', { responseType: 'json', form: { refresh_token: refreshToken, grant_type: 'refresh_token', client_id: clientId, client_secret: clientSecret } }).json()
       return { accessToken }
     })
   }
@@ -181,7 +181,10 @@ class Drive extends Provider {
       fn,
       tag,
       providerName: this.authProvider,
-      isAuthError: (response) => response.statusCode === 401,
+      isAuthError: (response) => (
+        response.statusCode === 401
+        || (response.statusCode === 400 && response.body?.error === 'invalid_grant') // Refresh token has expired or been revoked
+      ),
       getJsonErrorMessage: (body) => body?.error?.message,
     })
   }

+ 2 - 1
packages/@uppy/companion/src/server/provider/index.js

@@ -42,7 +42,7 @@ const providerNameToAuthName = (name, options) => { // eslint-disable-line no-un
  *
  * @param {Record<string, typeof Provider>} providers
  */
-module.exports.getProviderMiddleware = (providers) => {
+module.exports.getProviderMiddleware = (providers, grantConfig) => {
   /**
    *
    * @param {object} req
@@ -56,6 +56,7 @@ module.exports.getProviderMiddleware = (providers) => {
       const { allowLocalUrls } = req.companion.options
       req.companion.provider = new ProviderClass({ providerName, allowLocalUrls })
       req.companion.providerClass = ProviderClass
+      req.companion.providerGrantConfig = grantConfig[ProviderClass.authProvider]
 
       if (isOAuthProvider(ProviderClass.authProvider)) {
         req.companion.getProviderCredentials = getCredentialsResolver(providerName, req.companion.options, req)

+ 12 - 0
packages/@uppy/companion/src/server/provider/onedrive/index.js

@@ -13,6 +13,10 @@ const getClient = ({ token }) => got.extend({
   },
 })
 
+const getOauthClient = () => got.extend({
+  prefixUrl: 'https://login.live.com',
+})
+
 const getRootPath = (query) => (query.driveId ? `drives/${query.driveId}` : 'me/drive')
 
 /**
@@ -81,9 +85,17 @@ class OneDrive extends Provider {
 
   // eslint-disable-next-line class-methods-use-this
   async logout () {
+    // apparently M$ doesn't support programmatic oauth2 revoke
     return { revoked: false, manual_revoke_url: 'https://account.live.com/consent/Manage' }
   }
 
+  async refreshToken ({ clientId, clientSecret, refreshToken, redirectUri }) {
+    return this.#withErrorHandling('provider.onedrive.token.refresh.error', async () => {
+      const { access_token: accessToken } = await getOauthClient().post('oauth20_token.srf', { responseType: 'json', form: { refresh_token: refreshToken, grant_type: 'refresh_token', client_id: clientId, client_secret: clientSecret, redirect_uri: redirectUri } }).json()
+      return { accessToken }
+    })
+  }
+
   async #withErrorHandling (tag, fn) {
     return withProviderErrorHandling({
       fn,