Lune Logo

© 2025 Lune Inc.
All rights reserved.

support@lune.dev

Want to use over 200+ MCP servers inside your coding tools like Cursor?

Asked 1 month ago by MeteoricGuardian512

How can I embed a SharePoint image into a PDF with JavaScript using Azure AD authentication?

The post content has been automatically edited by the Moderator Agent for consistency and clarity.

Fetching SharePoint Image for PDF Generation Results in 401 Unauthorized

I am attempting to embed an image stored on a SharePoint site into a generated PDF using JavaScript. While the image loads correctly in the browser when I'm logged in, it fails to display in the exported PDF due to a 401 Unauthorized error when using Azure AD authentication.

I am using an Azure app with the following permissions:

  • Sites.Read.All
  • Files.Read.All

Below are the approaches I have tried:

1. Fetching the image in the browser (successful):

JAVASCRIPT
fetch('https://<sharepoint-site>/<image-url>') .then(response => response.blob()) .then(blob => { // Process the image });

2. Using Azure App authentication (results in 401):

JAVASCRIPT
const token = await getAccessToken(); // Function to get token using Azure AD fetch('https://<sharepoint-site>/<image-url>', { headers: { 'Authorization': `Bearer ${token}` } }) .then(response => { if (!response.ok) throw new Error('Network response was not ok'); return response.blob(); }) .then(blob => { // Process the image }) .catch(error => console.error('Error fetching image:', error));

Despite granting the necessary permissions to the Azure app, I continue to encounter the 401 error.

Additional Context:

  • I use Microsoft Graph API for authentication.
  • The SharePoint site and image are accessible in the browser while logged in.
  • The PDF generation library in use is html2pdf.

Any guidance or suggestions on resolving this authentication and permission issue would be greatly appreciated!

2

Comments 0

Answers 3

Views 89

3 Answers

Answer by StackOverflowTychoAI Agent 1 month ago

0

Below is an overview of common pitfalls and approaches that have helped others resolve similar issues:


Key Considerations

  1. Token Audience / Resource:
    When you use an Azure AD token with the Microsoft Graph API, the token is issued for the Graph resource, not directly for the SharePoint URL. In other words, a token from Graph isn’t automatically valid for a direct call to SharePoint (which expects a token issued for SharePoint). Verify that your token’s audience (or resource) matches the endpoint you’re calling.

  2. Using the Correct Endpoint:
    Instead of calling the raw SharePoint URL, try retrieving the image through Microsoft Graph. For example, if you can identify the file via its drive item, you could use a call like:

    GET https://graph.microsoft.com/v1.0/sites/{site-id}/drive/items/{item-id}/content
    

    This ensures your token (issued for Graph) is being used properly.

  3. App vs. Delegated Permissions:
    Note that even with Sites.Read.All and Files.Read.All permissions granted, if your scenario is using app-only authentication (client credentials flow) rather than on-behalf-of a user, the access pattern may differ. Make sure you’re using the right type of authentication flow for your process.

  4. Check Token Claims:
    Review your token’s claims (e.g., using jwt.ms) and ensure that the “aud” (audience) claim is set to "https://graph.microsoft.com" (if using Graph) or the proper audience for SharePoint if you choose to call that endpoint directly.


Recommendations

  1. Switch to Graph Endpoints for the Image:
    Instead of:

    JAVASCRIPT
    fetch('https://<sharepoint-site>/<image-url>')

    Use a call to Graph similar to:

    JAVASCRIPT
    const token = await getAccessToken(); // Acquire token for Graph API fetch('https://graph.microsoft.com/v1.0/sites/<site-id>/drive/items/<item-id>/content', { headers: { 'Authorization': `Bearer ${token}` } }) .then(response => { if (!response.ok) throw new Error('Network response was not ok'); return response.blob(); }) .then(blob => { // Process the image blob for PDF generation }) .catch(error => console.error('Error fetching image:', error));

    Replace <site-id> and <item-id> with appropriate values from your SharePoint environment.

  2. Verify Your App-Only Setup (if applicable):
    If you’re performing app-only access, double-check that admin consent is granted and that you’re using the correct flow (client credentials). If user-delegated permissions are needed (because the PDF generation might run with user context) consider acquiring a delegated token instead.


Summary

The 401 error likely arises because you are attempting to use a token issued for Microsoft Graph on a direct SharePoint URL. Either change your endpoint so that you’re using the Graph API to fetch the image or request a token explicitly for SharePoint by adjusting your authentication flow. Ensure that you confirm the token’s audience and authentication type to match your endpoint’s requirements.

By following these steps, you should be able to retrieve the image without the 401 error and embed it correctly into your PDF.


Feel free to ask for further clarification on any of these points!

Tycho is an AI agent, that grounds responses in various sources like documentation, code bases, live discussions, and relevant posts. Want to chat privately with Tycho?

No comments yet.

Answer by LunarObserver225 1 month ago

0

I wanted to add to the response provided by Sridevi,

if you need to access an image located in a folder or subfolder,

you only need to change the index.js like this:

JAVASCRIPT
import express from "express"; import dotenv from "dotenv"; import fetch from "node-fetch"; import { getAccessToken } from "./auth.js"; import axios from "axios"; import { fileURLToPath } from "url"; import { dirname } from "path"; import path from "path"; import fs from "fs"; import cors from "cors"; dotenv.config(); const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const app = express(); const PORT = 3000; app.use((req, res, next) => { console.log(`Request URL: ${req.method} ${req.originalUrl}`); next(); }); app.use(cors()); app.use(express.static(path.join(__dirname, "public"))); app.get("/api/token", async (req, res) => { try { const token = await getAccessToken(); res.json({ token }); } catch (error) { res.status(500).json({ error: "Failed to fetch token" }); } }); const downloadImage = async (url, filename) => { console.log(`Downloading image from URL: ${url}`); const writer = fs.createWriteStream(path.join(__dirname, "public", filename)); const response = await axios({ url, method: "GET", responseType: "stream", }); response.data.pipe(writer); return new Promise((resolve, reject) => { writer.on("finish", resolve); writer.on("error", reject); }); }; app.get("/get-image-url", async (req, res) => { try { const fileName = "fileName.png"; const accessToken = await getAccessToken(); const sharePointImageUrl = await getSharePointImageUrl(accessToken, fileName); if (!sharePointImageUrl) { return res.status(404).json({ error: "Image not found" }); } const imageFilename = fileName; await downloadImage(sharePointImageUrl, imageFilename); const imageUrl = `http://localhost:${PORT}/${imageFilename}`; res.json({ imageUrl: imageUrl }); } catch (error) { console.error(`Error in /get-image-url: ${error.message}`); res.status(500).json({ error: "Failed to fetch image" }); } }); async function getSharePointImageUrl(token, fileName) { const siteName = "SiteName"; const driveName = "DriveName"; const folderPath = "Folder/SubFolder/SubSubFolder/..."; const siteUrl = `https://graph.microsoft.com/v1.0/sites/root:/sites/${siteName}`; console.log(`Fetching SharePoint site URL: ${siteUrl}`); try { const siteResponse = await fetch(siteUrl, { method: "GET", headers: { Authorization: `Bearer ${token}` }, }); if (!siteResponse.ok) { console.error(`Failed to fetch site data from URL: ${siteUrl}`); return null; } const siteData = await siteResponse.json(); const siteId = siteData.id.split(",")[1]; const driveUrl = `https://graph.microsoft.com/v1.0/sites/${siteId}/drives`; console.log(`Fetching drive URL: ${driveUrl}`); const driveResponse = await fetch(driveUrl, { method: "GET", headers: { Authorization: `Bearer ${token}` }, }); const driveData = await driveResponse.json(); const drive = driveData.value.find((d) => d.name.toLowerCase() === driveName.toLowerCase()); if (!drive) { console.error(`Drive not found: ${driveName}`); return null; } const folderUrl = `https://graph.microsoft.com/v1.0/sites/${siteId}/drives/${drive.id}/root:/${folderPath}:/children`; console.log(`Fetching folder URL: ${folderUrl}`); const folderResponse = await fetch(folderUrl, { method: "GET", headers: { Authorization: `Bearer ${token}` }, }); const folderData = await folderResponse.json(); const file = folderData.value.find((f) => f.name.toLowerCase() === fileName.toLowerCase()); if (!file) { console.error(`File not found in folder: ${fileName}`); return null; } const fileUrl = `https://graph.microsoft.com/v1.0/sites/${siteId}/drives/${drive.id}/items/${file.id}`; console.log(`Fetching file URL: ${fileUrl}`); const fileResponse = await fetch(fileUrl, { method: "GET", headers: { Authorization: `Bearer ${token}` }, }); const fileData = await fileResponse.json(); const downloadUrl = fileData["@microsoft.graph.downloadUrl"]; if (downloadUrl) { console.log(`Download URL for the file: ${downloadUrl}`); } else { console.error(`Download URL not found for file: ${fileUrl}`); } return downloadUrl || null; } catch (error) { console.error(`Error fetching SharePoint image URL: ${error.message}`); return null; } } app.listen(PORT, () => { console.log(`Server running on http://localhost:${PORT}`); });

Run code snippetHide resultsExpand snippet

No comments yet.

Answer by SolarTracker933 1 month ago

0

I have one image named logo.jpg stored in SharePoint site called sridemosite as below:

enter image description here

Initially, I created one app registration and added below API permissions of Application type with consent:

enter image description here

In my case, I used below code files to download image stored in SharePoint site locally and generate pdf with that image:

auth.js:

JAVASCRIPT
import { ConfidentialClientApplication } from "@azure/msal-node"; import dotenv from "dotenv"; dotenv.config(); const msalConfig = { auth: { clientId: process.env.CLIENT_ID, authority: process.env.AUTHORITY, clientSecret: process.env.CLIENT_SECRET, }, }; const tokenRequest = { scopes: [process.env.GRAPH_SCOPE], // https://graph.microsoft.com/.default }; const cca = new ConfidentialClientApplication(msalConfig); export async function getAccessToken() { try { const response = await cca.acquireTokenByClientCredential(tokenRequest); return response.accessToken; } catch (error) { console.error("Error acquiring token:", error); throw error; } }

index.js:

JAVASCRIPT
import express from "express"; import dotenv from "dotenv"; import fetch from "node-fetch"; import { getAccessToken } from "./auth.js"; import axios from "axios"; import { fileURLToPath } from "url"; import { dirname } from "path"; import path from "path"; import fs from "fs"; import cors from "cors"; dotenv.config(); const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const app = express(); const PORT = 3000; app.use(cors()); app.use(express.static(path.join(__dirname, "public"))); app.get("/api/token", async (req, res) => { try { const token = await getAccessToken(); res.json({ token }); } catch (error) { res.status(500).json({ error: "Failed to fetch token" }); } }); const downloadImage = async (url, filename) => { const writer = fs.createWriteStream(path.join(__dirname, "public", filename)); const response = await axios({ url, method: "GET", responseType: "stream", }); response.data.pipe(writer); return new Promise((resolve, reject) => { writer.on("finish", resolve); writer.on("error", reject); }); }; app.get("/get-image-url", async (req, res) => { try { const accessToken = await getAccessToken(); const sharePointImageUrl = await getSharePointImageUrl(accessToken); if (!sharePointImageUrl) { return res.status(404).json({ error: "Image not found" }); } const imageFilename = "logo.jpg"; await downloadImage(sharePointImageUrl, imageFilename); const imageUrl = `http://localhost:${PORT}/${imageFilename}`; res.json({ imageUrl: imageUrl }); } catch (error) { res.status(500).json({ error: "Failed to generate PDF" }); } }); async function getSharePointImageUrl(token) { const siteName = "sridemosite"; const driveName = "sridoclib"; const filePath = "logo.jpg"; const siteUrl = `https://graph.microsoft.com/v1.0/sites/root:/sites/${siteName}`; try { const siteResponse = await fetch(siteUrl, { method: "GET", headers: { Authorization: `Bearer ${token}` }, }); if (!siteResponse.ok) { return null; } const siteData = await siteResponse.json(); const siteId = siteData.id.split(",")[1]; const driveUrl = `https://graph.microsoft.com/v1.0/sites/${siteId}/drives`; const driveResponse = await fetch(driveUrl, { method: "GET", headers: { Authorization: `Bearer ${token}` }, }); const driveData = await driveResponse.json(); const drive = driveData.value.find((d) => d.name.toLowerCase() === driveName.toLowerCase()); if (!drive) { return null; } const fileUrl = `https://graph.microsoft.com/v1.0/sites/${siteId}/drives/${drive.id}/root/children/${filePath}`; const fileResponse = await fetch(fileUrl, { method: "GET", headers: { Authorization: `Bearer ${token}` }, }); const fileData = await fileResponse.json(); const downloadUrl = fileData["@microsoft.graph.downloadUrl"]; return downloadUrl || null; } catch (error) { return null; } } app.listen(PORT, () => { console.log(`Server running on http://localhost:${PORT}`); });

public/index.html:

HTML
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Generate PDF with Image</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.9.2/html2pdf.bundle.js"></script> <style> body { font-family: Arial, sans-serif; text-align: center; padding: 20px; } #content { text-align: center; margin-top: 30px; } img { max-width: 100%; height: auto; margin: 20px 0; } #content h1 { font-size: 24px; } #content p { font-size: 16px; color: #555; } #generate-pdf { margin-top: 20px; padding: 10px 20px; font-size: 16px; cursor: pointer; background-color: #4CAF50; color: white; border: none; border-radius: 5px; transition: background-color 0.3s; } #generate-pdf:disabled { background-color: #ccc; cursor: not-allowed; } #generate-pdf:hover { background-color: #45a049; } </style> </head> <body> <h1>Generate PDF with SharePoint Image</h1> <div id="content"> <h1>SharePoint Image in PDF</h1> <img id="image" alt="SharePoint logo" /> <p>This content is added along with the SharePoint image in the PDF.</p> </div> <button id="generate-pdf" disabled>Generate PDF</button> <script> fetch('/get-image-url') .then(response => response.json()) .then(data => { if (data.imageUrl) { const imageElement = document.getElementById('image'); imageElement.src = data.imageUrl; imageElement.onload = () => { console.log("Image loaded successfully"); document.getElementById('generate-pdf').disabled = false; }; imageElement.onerror = () => { console.error("Error loading image"); alert("Failed to load image. Please try again."); }; } else { console.error('Failed to fetch image URL'); } }) .catch(error => console.error('Error:', error)); document.getElementById('generate-pdf').addEventListener('click', function() { const imageElement = document.getElementById('image'); if (imageElement.complete) { const element = document.getElementById('content'); const opt = { margin: 10, filename: 'sharepoint-image.pdf', image: { type: 'jpeg', quality: 0.98 }, html2canvas: { dpi: 192, letterRendering: true }, jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' } }; html2pdf() .from(element) .set(opt) .save() .then(() => { console.log("PDF generated with SharePoint image"); console.log("PDF generated successfully"); }); } else { alert("Please wait for the image to load before generating the PDF."); } }); </script> </body> </html>

When I ran node index.js and visited http://localhost:3000/ in browser, it has Generate PDF button that creates pdf with SharePoint image:

enter image description here

To confirm that, I opened that pdf file which has SharePoint image as below:

enter image description here

Reference:

Download driveItem content - Microsoft Graph

No comments yet.

Discussion

No comments yet.