Authorization header in img src link

JavascriptHtmlJwt

Javascript Problem Overview


I have an api that uses jwt for authencation. I am using this api for a vuejs app. I am trying to display an image in the app using

<img src="my/api/link" />

But the api expects Authorization header with jwt token in it.

Can I add headers to browser request like this(Answer to few questions here has made me believe it's not possible)?

Is there any way around it(using js) or should i change the api itself?

Javascript Solutions


Solution 1 - Javascript

You can not perform authentication on images which are directly used as href in img tag. If you really want this type of authentication on your images, then it's better to fetch them using ajax and then embed in your html.

Solution 2 - Javascript

By default browsers are sending cookies. You can prevent cookie sending in fetch if you set header's {credentials: 'omit'}. MDN

Full fetch example:

const user = JSON.parse(localStorage.getItem('user'));
let headers = {};

if (user && user.token) {
  headers = { 'Authorization': 'Bearer ' + user.token };
} 

const requestOptions = {
    method: 'GET',
    headers: headers,
    credentials: 'omit'
};

let req = await fetch(`${serverUrl}/api/v2/foo`, requestOptions);
if (req.ok === true) {
...

Now, when you are login in, in your website, the webapp could save to credentials into both localStorage and cookie. Example:

let reqJson = await req.json();
// response is: {token: 'string'}
//// login successful if there's a jwt token in the response
if (reqJson.token) {
    // store user details and jwt token in local storage to keep user logged in between page refreshes
    localStorage.setItem('user', JSON.stringify({token: reqJson.token}));
    document.cookie = `token=${reqJson.token};`; //set the cookies for img, etc
}

So your webapp uses localStorage, just like your smartphone application. Browser gets all the static contents (img, video, a href) by sending cookies by default.

On the server side, you can copy the cookie to authorization header, if there is none.

Node.js+express example:

.use(function(req, res, next) { //function setHeader
  if(req.cookies && req.headers &&
     !Object.prototype.hasOwnProperty.call(req.headers, 'authorization') &&
     Object.prototype.hasOwnProperty.call(req.cookies, 'token') &&
     req.cookies.token.length > 0
   ) {
    //req.cookies has no hasOwnProperty function,
    // likely created with Object.create(null)
    req.headers.authorization = 'Bearer ' + req.cookies.token.slice(0, req.cookies.token.length);
  }
  next();
})

I hope it helps someone.

Solution 3 - Javascript

You can use a Service Worker to intercept the img fetchs and add the Authorization header with the JWT token before hitting the server. Described in:

Solution 4 - Javascript

<img src="/api/images/yourimage.jpg?token=here-your-token">

In the backend you validate JWT from queryparam.

Solution 5 - Javascript

A workaround I often use is by leveraging a so-called nonce API endpoint. When calling this endpoint from the authenticated client, a short living string (could be a guid) is generated (for instance 30 seconds) and returned. Server-side you could of course add current user data to the nonce if you wish.

The nonce is then added to the image's query string and be validated server-side. The cost of this workaround is an extra API call.The main purpose of the workaround however is an increased security warrantee. Works like a charm ;) .

Solution 6 - Javascript

This is my solution based on Tapas' answer and this question How to display Base64 images in HTML?:

let jwtHeader = {headers: { Authorization: "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpX..."}
let r = await axios.get(`/path/image`, {...jwtHeader, responseType:"arraybuffer"});
let d = Buffer.from(r.data).toString('base64');
let a = document.createElement('img');

a.src = `data:image/png;base64, ${d}`;
a.width = 300;
a.height = 300;
document.getElementById("divImage").appendChild(a);

In this case the html would have a <div id="divImage">

Solution 7 - Javascript

There is another one method adds headers to HTTP request. Is it "Intercept HTTP requests". https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Intercept_HTTP_requests

Solution 8 - Javascript

Try this

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>测试获取图片</title>
		<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
	</head>
	<body>
		<img id="test-img" src="" />
		<script>
		var request = new XMLHttpRequest();
		request.open('GET','http://127.0.0.1/appApi/profile/cust/idcard/2021/12/30/533eed96-da1b-463b-b45d-7bdeab8256d5.jpg', true);
		request.setRequestHeader('token', 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NDA5MTg1NTgsInVzZXJpZCI6IjMxIn0.TQmQE9E1xQwvVeAWRov858W2fqYpSMxZPCGlgvtcUDc');
		request.responseType = 'arraybuffer';
		request.onload = function(e) {
		    var data = new Uint8Array(this.response);
		    var raw = String.fromCharCode.apply(null, data);
		    var base64 = btoa(raw);
		    var src = "data:image;base64," + base64;
		
		    document.getElementById("test-img").src = src;
		};
		
		request.send();
		</script>
	</body>
</html>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRagasView Question on Stackoverflow
Solution 1 - JavascriptTapasView Answer on Stackoverflow
Solution 2 - JavascriptarcolView Answer on Stackoverflow
Solution 3 - JavascriptNahuel GrecoView Answer on Stackoverflow
Solution 4 - JavascriptAlexysView Answer on Stackoverflow
Solution 5 - JavascriptDotBertView Answer on Stackoverflow
Solution 6 - JavascriptLuis F.View Answer on Stackoverflow
Solution 7 - JavascriptPanteleimon.KylishView Answer on Stackoverflow
Solution 8 - Javascriptuser7721763View Answer on Stackoverflow