41

I have an Ionic app where it fetches the data from remote server and displays it on Ionic html page.

The remote URL is like this:

http://foo.com/api/content/1

This will give me a JSON object of "content" and will be used further in the html page of Ionic app.

It is being used like this on html page inside Ionic app:

<div class="article-desc">
  <p [innerHtml]="myObject?.Body"></p>
</div>

"myObject" is the JSON object of response received from the server.

The "Body" field contains the HTML to be displayed in the paragraph. This "HTML" field is being returned from server only along with the entire "content" object.

"Body" field can have content like this:

<p>blah blah <img src="http://foo.com/image/1"/> blah blah <img src="http://foo.com/image/2"/>blah blah blah </p>

You can see from the above example that the images are there in that html.

I have no issue rendering the html from that field to Ionic Page.

I have one issue here that my images are not being rendered there.

Here is why..

My app is locked for Guest users so for each request I need to send an Authorization header in order to authenticate it and in this case all the images are not able to render because each image request will be treated as guest here for server.

Can you suggest a common place where all my images and other sources like there in html should pass through and can send authorization header along with it to server.

I already have the Authorization Token in local storage item.

My goal is to send authorization header to each external source (image here) present in that Body field when it renders in Ionic app.

Sunny Drall
  • 844
  • 9
  • 20
Raghav
  • 8,772
  • 6
  • 82
  • 106
  • You can implement the interceptor which monitors all the requests and you can attach header there, you will have to configure once and it will work for all the http calls made from your app. Have you considered writing this in your application? – Deepak Jha Dec 14 '17 at 11:25
  • That seems a good idea. Do you have some reference from Ionic official documentation for doing the same? – Raghav Dec 14 '17 at 11:28
  • well I can guide I think, I feel it will be same for ionic and angular after all it is angular at the end of the day, let me write an answer for you. – Deepak Jha Dec 14 '17 at 11:29
  • Well, I am reading the following post https://medium.com/tableless/angular-4-and-ionic-3-add-custom-headers-to-http-requests-11aaf93798d9 in order to implement something like this. Waiting more good suggestions if you have.. – Raghav Dec 14 '17 at 11:31
  • Actually that link is enough, i can explain what interceptor is in my answer though. But the link you are referring looks enough as far as implementation is concerned. – Deepak Jha Dec 14 '17 at 11:33

3 Answers3

90

1) Create interceptor which sets authorization header

import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

        request = request.clone({
            setHeaders: {
                Authorization: `Bearer <your token>`
            }
        });

        return next.handle(request);
    }
}

Instead of <your token> you should inject your AuthService into this interceptor, for example this.authService.getToken(), which loads token from local storage.

2) Implement "secure" pipe which gets image as blob and creates an object url of that blob that can be used in the src attribute

import { Pipe, PipeTransform } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
import { Observable } from 'rxjs/Observable';

@Pipe({
    name: 'secure'
})
export class SecurePipe implements PipeTransform {

    constructor(private http: HttpClient, private sanitizer: DomSanitizer) { }

    transform(url): Observable<SafeUrl> {
        return this.http
            .get(url, { responseType: 'blob' })
            .map(val => this.sanitizer.bypassSecurityTrustUrl(URL.createObjectURL(val)));
    }

}

3) Use it

<img [attr.src]="'your link' | secure | async" />
Sunny Drall
  • 844
  • 9
  • 20
Milan Hlinák
  • 4,260
  • 1
  • 30
  • 41
  • 3
    Nice solution, thanks! I used your SecurePipe and inserted the authorization header in transform function before calling the http get. It worked too. It may be an alternative for those who does not want to implement an interceptor. – heringer May 30 '19 at 18:16
  • 1
    Worked for me and is also applicable to the `audio` tag. – Mavv3006 Mar 05 '22 at 10:32
  • It is worth mentioning using an async pipe will cause the browser not to cache the image. – SDekov Jun 03 '22 at 22:28
  • @SDekov are you sure that your resource had Cache-Control? – m1ld Dec 27 '22 at 19:07
6

1st Option: Look for "URL signing"

The idea is that, when you use <img src="http://foo.com/image/1"> there is no way to pass the authorization headers. So instead, you make post request to your backend to ask for a temporary public link for the image and put this link as source of image.

Here is an example flow

  1. I need to show "http://foo.com/image/1"

  2. From the browser, make a post request to backend, let them know you are an authorized client (include the authorization header), and ask for a temporary url that will show the image publicly

  3. From the backend, generate a signed url that is valid for a limited time and does not require authorization headers to show the image.

  4. Use the temporary signed url you just received as src of the img tag.

2nd Option: Download the image and use blob URL

Answers to this question will tell you about it: Force HTTP interceptor in dynamic ngSrc request

Hasan
  • 2,444
  • 3
  • 30
  • 44
  • Shouldn't the interceptors like https://medium.com/tableless/angular-4-and-ionic-3-add-custom-headers-to-http-requests-11aaf93798d9 work? The requirement I am having seems to be a common one. I wonder how others are implementing it with their content having images pointing to their server. – Raghav Dec 14 '17 at 11:34
  • 1
    That intercepts the requests that are done in the javascript part. It will not intercept the request that is done by the browser as a result of src of img tag, @Raghav – Hasan Dec 14 '17 at 11:37
  • And yes this is a common problem and this is one way to solve it – Hasan Dec 14 '17 at 11:38
  • As you can see I am just binding "

    " i.e the Body to inner html there. Where should I intercept html and do a post request.
    – Raghav Dec 14 '17 at 11:43
  • @Raghav there is no way to intercept a browser's get call to src of img tag. I updated my answer with a second option which would not need any modifications to the backend service – Hasan Dec 14 '17 at 11:47
  • Bases on your 2nd suggestion I guess I need to do this... parse the html, replace src tag of img with http-src tag with same url, Right? – Raghav Dec 14 '17 at 11:54
  • @Raghav, parse the html, find the original url in src of img tag, download the image using javascript (including your auth header) and encode it as blob url, and replace the original url in src of img tag with this blob url. It should work this way – Hasan Dec 14 '17 at 11:56
  • @Raghav, Forget what I just said. I didn't see that answer on the question I shared. Yes you are right. Just implement that http-src thingy as the answer suggests, and replace src with html-src – Hasan Dec 14 '17 at 12:00
  • Before trying that.. Wouldn't it hamper the performance converting the image to blob? Will image still be coming from cache the next time? – Raghav Dec 14 '17 at 12:12
  • @Raghav you are right, it won't be cached, on the other hand if you use the option 1 I suggested, catching could be valid only for the duration of the validity of the signed url, because next time the url will change, too. This is the trade off you have to decide, performance vs security – Hasan Dec 14 '17 at 12:32
0

Here is how you write an interceptor,

  1. Need to extend a class called HttpInterceptor provided in angular 4/5.
  2. Override a method called intercept,

It will add the header in all your http request, this would ideally be the place where you would perhaps need to put sanitation logic, for an example if you wish to put just certain request with that header you can decide that in intercept method.

export class YourInterceptor implements HttpInterceptor{    
    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>{
const NewRequest= req.clone({ headers: req.headers.set(‘Auth-Token’, ‘YourAuthToken’) });

return next.handle(NewRequest); }

After this you need to register this in your app.module.ts file in the manner given below,

import { YourInterceptor } from './Your-interceptor';

now go to the @NgModule section and do this in your provider array, it would be to the provider array as given below,

providers: [{provide: HTTP_INTERCEPTORS,useClass: YourInterceptor,multi: true}],

Now restart your app and whatever http call you make it will have a control inside which will intercept your code and you will be able to sail through.

Deepak Jha
  • 1,539
  • 12
  • 17