0

I've been trying to integreate Cloudinary via angular-upload into my AngularJS app. However, I have encountered this error when attempting to upload from my app:

Authorization is not allowed by Access-Control-Allow-Headers

At first I thought this was a grunt issue since I was using http-server to run the Cloudinary sample app, which uploaded successfully, but I have now realized this is more likely a result of using Auth0's angular-jwt implementation. This is attaching the Authorization header to all requests, which cloudinary does not accept. I found someone with a very similar problem here

https://github.com/DaftMonk/generator-angular-fullstack/issues/931

however, I'm having trouble figuring out how to adapt such a solution to angular-jwt. I call

$httpProvider.interceptors.push('jwtInterceptor');

in my app.config, but I want it to exclude requests to https://api.cloudinary.com/v1_1/

This is the angular-jwt distribution:

(function() {


// Create all modules and define dependencies to make sure they exist
// and are loaded in the correct order to satisfy dependency injection
// before all nested files are concatenated by Grunt

// Modules
angular.module('angular-jwt',
    [
        'angular-jwt.interceptor',
        'angular-jwt.jwt'
    ]);

 angular.module('angular-jwt.interceptor', [])
  .provider('jwtInterceptor', function() {

    this.urlParam = null;
    this.authHeader = 'Authorization';
    this.authPrefix = 'Bearer ';
    this.tokenGetter = function() {
      return null;
    }

    var config = this;

    this.$get = ["$q", "$injector", "$rootScope", function ($q, $injector, $rootScope) {
      return {
        request: function (request) {
          if (request.skipAuthorization) {
            return request;
          }

          if (config.urlParam) {
            request.params = request.params || {};
            // Already has the token in the url itself
            if (request.params[config.urlParam]) {
              return request;
            }
          } else {
            request.headers = request.headers || {};
            // Already has an Authorization header
            if (request.headers[config.authHeader]) {
              return request;
            }
          }

          var tokenPromise = $q.when($injector.invoke(config.tokenGetter, this, {
            config: request
          }));

          return tokenPromise.then(function(token) {
            if (token) {
              if (config.urlParam) {
                request.params[config.urlParam] = token;
              } else {
                request.headers[config.authHeader] = config.authPrefix + token;
              }
            }
            return request;
          });
        },
        responseError: function (response) {
          // handle the case where the user is not authenticated
          if (response.status === 401) {
            $rootScope.$broadcast('unauthenticated', response);
          }
          return $q.reject(response);
        }
      };
    }];
  });

 angular.module('angular-jwt.jwt', [])
  .service('jwtHelper', function() {

    this.urlBase64Decode = function(str) {
      var output = str.replace(/-/g, '+').replace(/_/g, '/');
      switch (output.length % 4) {
        case 0: { break; }
        case 2: { output += '=='; break; }
        case 3: { output += '='; break; }
        default: {
          throw 'Illegal base64url string!';
        }
      }
      return decodeURIComponent(escape(window.atob(output))); //polifyll https://github.com/davidchambers/Base64.js
    }


    this.decodeToken = function(token) {
      var parts = token.split('.');

      if (parts.length !== 3) {
        throw new Error('JWT must have 3 parts');
      }

      var decoded = this.urlBase64Decode(parts[1]);
      if (!decoded) {
        throw new Error('Cannot decode the token');
      }

      return JSON.parse(decoded);
    }

    this.getTokenExpirationDate = function(token) {
      var decoded;
      decoded = this.decodeToken(token);

      if(typeof decoded.exp === "undefined") {
        return null;
      }

      var d = new Date(0); // The 0 here is the key, which sets the date to the epoch
      d.setUTCSeconds(decoded.exp);

      return d;
    };

    this.isTokenExpired = function(token, offsetSeconds) {
      var d = this.getTokenExpirationDate(token);
      offsetSeconds = offsetSeconds || 0;
      if (d === null) {
        return false;
      }

      // Token expired?
      return !(d.valueOf() > (new Date().valueOf() + (offsetSeconds * 1000)));
    };
  });

}());

Help?

1 Answer 1

4

jwtInterceptor checks the request for a skipAuthorization flag, and does not send the Authorization header if it was set to true.

Build your $http call like

$http({
    url: 'https://api.cloudinary.com/v1_1/',
    skipAuthorization: true,
    method: 'POST',
    // ... etc
}).then(successCallback, errorCallback);

- More at angular-jwt Docs

Sign up to request clarification or add additional context in comments.

2 Comments

Wow, so easy and I spent so much time trying to figure it out. Thanks!
I also had a very hard time to figure a similar issue. I was developing both client and server side code, and the server side code expected valid Authorization header for the OPTIONS request (performed by the framework before any other request). I removed the Authorization validation for OPTIONS and it did the job.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.