Request Image Files with Angular 2 and an Bearer Access Token

20 Dec

When using a full-fledged framework like Angular 2 to talk to your server with AccessTokens, you might not use cookies. In this case, any image requests to your server with an <img />  tag will fail if these need to be authorized.

I have coded a little UrlService and a pipe to help you out. You will need to adapt to your services.

The Service

This code will allow you to request an image with your access token, then return Observable<string>  which will contain a Blob URL. This is using the FileAPI with the method createObjectURL.

Notice also that we return a function for revoking the URL. This way, we avoid memory leaks.

The Pipe

Let’s create a pipe that uses the service above. I have adapted the “async” pipe so you don’t have to write | secure | async  all the time.

You will notice that this pipe is not pure. But the main code is in internalTransform() . We will check if you ask for a different Url, if not, we will not make multiple GET request to your server.

We also use the DomSanitizer because Angular will complain that the blob scheme is unsafe.

Register Services and Pipes

Yep, don’t forget to do that…

The HTML

Simply use the secure pipe like this

Make sure to use the [src]  since you are not actually returning a string but a “ SafeUrl “. Your image is now requested through your UrlHelper service with an access token!

 

Jean-Sébastien Goupil (11 Posts)

Web Architect Consultant - Passionate about web projects! Expert in barcodes, automation, and JavaScript

  • Thank you for this! I’ve been looking for a way to only allow users logged in to my angular 2 app (via Auth0) to see their profile photos. Do you think your solution would be an overkill, and that there’s an even simpler method for my use case?

    • jsgoupil

      I haven’t used Auth0, they might have something similar to make requests to the server. But the solution I propose here is quite simple and it works great in all browsers.
      The most important part of this blog is the UrlHelperService, the pipe is big because I copied the idea of an AsyncPipe.

      • Well sure, but without the pipe one would have to write a lot of code for every link to function as intended. Imagine if you would like to protect all images on your webpage for instance!

  • kris phillips

    Did you have any issues with the “missing image” icon while the ajax call is in flight? basically I see the not found icon for a split second before the image actually loads.

    • jsgoupil

      I haven’t seen that, however, I found that Chrome was making “null” requests to my server. I have replaced this line

      private _result: BehaviorSubject = new BehaviorSubject(null);

      by

      private _result: BehaviorSubject = new BehaviorSubject(”);

      does that fix the problem you are experiencing? Which browser do you see the missing image?

      • kris phillips

        That did it! I’m using chrome and had noticed the null http requests in the network traffic as well, glad this cleans that up too.

        Additionally I opted to use an AuthHttp service that I’m already using for applying tokens to all http requests (courtesy of https://github.com/sureshchahal/angular2-adal) rather than your sample url service. This meant I had to modify the internalTransform subscription to account to include the URL.createObjectURL call:

        this._internalSubscription = this.http.get(url, {
        responseType: ResponseContentType.Blob
        } as RequestOptionsArgs).subscribe(m => {
        var url = URL.createObjectURL(m.blob());
        let sanitized = this.sanitizer.bypassSecurityTrustUrl(url);
        this._result.next(sanitized);
        });

        where this.http is my AuthHttp service.

        I have to admit some of the observable code it still black magic to me, but it does work. Thanks for the quick response and sharing your solution.

        • jsgoupil

          You should keep the url-helper service and change that file to introduce the AuthHttp instead. You dropped the revokeObjectURL call which could be problematic.

          • kris phillips

            You’re right I missed that. I have taken the approach you recommended – could you describe the circumstances of when that will get called?

          • jsgoupil

            It’s called when the observable is destroyed. Put a debugger statement in the return function, you will see when it happens.

  • Andrey Kolybelnikov

    Great post! However, facing TypeErrors trying to implement the service:
    1. Angular http expects 1-2 arguments but got 3, and a ? at the end of one of the arguments won’t fix this either
    2. type Headers has no properties in common with type ‘RequestOptionsArgs’
    Could things have been change since the latest release perhaps?

    • Andrey Kolybelnikov

      Solved this by declaring the request options like so:

      this.options = new RequestOptions({
      method: RequestMethod.Get,
      url: url,
      headers: this.headers,
      responseType: ResponseContentType.Blob
      });

      importing {ResponseContentType, Http, RequestOptions, RequestMethod, Headers, Request}

      and passing the options like so:
      this.http.request(new Request(this.options))

      • Andrey Kolybelnikov

        I can’t seem to get any positive results. Implemented as shown above, but no token is appended to the get requests to the back-end:

        HTTP401: DENIED – The requested resource requires user authentication.

        I’m referring images in my view like so:

        where sku.thumblink is taken from my ‘sku’ model propertiesand is a usual url.

        • Andrey Kolybelnikov

          Finally, solved all the issues and the pipe works like a charm! Thank you so much. You’re a hero.

  • Silver

    Thanks, I was looking for a solution like this one!! @andrey_kolybelnikov:disqus I had the same issue and your fix worked, Thanks!

  • Yogesh Kumar

    Thanks for a great post. I got 95% success (getting everything progressed as per the blog) but failed to populate the image as src is being resolved only to be “unknown”. I managed to get my authorized image sent back as I can see long binary data in my response body. Badly stuck.
    My blob that is being prepared and returned as the observer.next as “blob:http://localhost:4300/cb7fd452-1c5e-49f4-a0fb-68ce4e84480c“. I don’t know how to debug it further. Any help will be highly appreciated.