I am trying to book an event in Outlook calendar through Graph API. I have created the application and granted the permission as shown in the screenshot.
The application has both Delegated permission and Application permission
Here is the code that I am using.
import axios from 'axios';
import querystring from 'querystring';
const tenantId = '123456'; // Your Directory (Tenant) ID
const clientId = '-558d64288141'; // Your Application (Client) ID
const clientSecret = 'ZXc8Q~t'; // Your Application Secret Value
async function getAccessToken() {
const tokenEndpoint = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;
const params = {
grant_type: 'client_credentials',
client_id: clientId,
client_secret: clientSecret,
scope: 'https://graph.microsoft.com/.default'
};
const response = await axios.post(tokenEndpoint, querystring.stringify(params), {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
return response.data.access_token;
}
async function bookCalendarEvent(accessToken, eventDetails) {
const endpoint = 'https://graph.microsoft.com/v1.0/users/schedule@test.com/events'; // Updated endpoint
const response = await axios.post(endpoint, eventDetails, {
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json'
}
});
return response.data;
}
export const handler = async (event) => {
try {
// Get access token using client credentials
const accessToken = await getAccessToken();
// Sample event details
const eventDetails = {
subject: 'Meeting with Lambda',
body: {
contentType: 'HTML',
content: 'This is a meeting scheduled by AWS Lambda'
},
start: {
dateTime: '2024-08-27T09:00:00', // Sample start date and time
timeZone: 'UTC'
},
end: {
dateTime: '2024-08-27T10:00:00', // Sample end date and time
timeZone: 'UTC'
},
attendees: [
{
emailAddress: {
address: 'example@domain.com', // Sample attendee email
name: 'John Doe'
},
type: 'required'
}
]
};
// Book the calendar event
const result = await bookCalendarEvent(accessToken, eventDetails);
return {
statusCode: 200,
body: JSON.stringify({
message: 'Event booked successfully',
eventId: result.id
})
};
} catch (error) {
console.error('Error booking calendar event:', error);
return {
statusCode: 500,
body: JSON.stringify({
message: 'Error booking event',
error: error.message
})
};
}
};
But when I run the AWS Lambda I get this error
{
"statusCode": 500,
"body": "{\"message\":\"Error booking event\",\"error\":\"Request failed with status code 403\"}"
}
So I am not sure what I am doing wrong Any help thanks
I agree with @juunas, you need to switch to delegated flows like authorization code flow to have access to only signed-in user's calendar.
I registered one Azure AD application and granted Calendars.ReadWrite
permission of Delegated type as below:
Now, I added redirect URI as http://localhost:3000/callback in Web
platform of application like this:
In my case, I used below modified code that works with authorization code flow to acquire token and books event in signed-in user calendar:
index.js:
const express = require('express');
const axios = require('axios');
const querystring = require('querystring');
const app = express();
const port = 3000;
let storedAccessToken = '';
const tenantId = 'tenantID';
const clientId = 'appID';
const clientSecret = 'secret';
const redirectUri = 'http://localhost:3000/callback';
app.get('/login', async (req, res) => {
const authorizationUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize?` +
querystring.stringify({
client_id: clientId,
response_type: 'code',
redirect_uri: redirectUri,
response_mode: 'query',
scope: 'openid profile offline_access Calendars.ReadWrite',
state: '12345'
});
const { default: open } = await import('open');
await open(authorizationUrl);
res.send('Opening Microsoft login page...');
});
app.get('/callback', async (req, res) => {
const code = req.query.code;
async function getAccessToken(code) {
const tokenEndpoint = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;
const response = await axios.post(tokenEndpoint, querystring.stringify({
grant_type: 'authorization_code',
client_id: clientId,
client_secret: clientSecret,
code: code,
redirect_uri: redirectUri,
scope: 'openid profile offline_access Calendars.ReadWrite'
}), {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
});
return response.data.access_token;
}
try {
const accessToken = await getAccessToken(code);
console.log('Access Token:', accessToken);
storedAccessToken = accessToken;
res.send('Login successful! You can now book an event.');
} catch (error) {
console.error('Error obtaining access token:', error.response ? error.response.data : error.message, error.stack);
res.status(500).send('Error obtaining access token: ' + error.message);
}
});
app.get('/book-event', async (req, res) => {
try {
if (!storedAccessToken) {
return res.status(401).json({ message: 'User is not authenticated. Please login first.' });
}
console.log('Using Stored Access Token:', storedAccessToken);
const eventDetails = {
subject: 'Meeting with Lambda',
body: {
contentType: 'HTML',
content: 'This is a meeting scheduled by AWS Lambda'
},
start: {
dateTime: '2024-08-27T09:00:00',
timeZone: 'UTC'
},
end: {
dateTime: '2024-08-27T10:00:00',
timeZone: 'UTC'
},
attendees: [
{
emailAddress: {
address: 'sridevi.maxxxxxxxxx@gmail.com',
name: 'Sridevi'
},
type: 'required'
}
]
};
const calendarEndpoint = `https://graph.microsoft.com/v1.0/me/events`;
const response = await axios.post(calendarEndpoint, eventDetails, {
headers: {
Authorization: `Bearer ${storedAccessToken}`,
'Content-Type': 'application/json'
}
});
res.json({ message: 'Event booked successfully', eventId: response.data.id });
} catch (error) {
console.error('Error booking event:', error.response ? error.response.data : error.message, error.stack);
res.status(500).json({ message: 'Error booking event', error: error.message });
}
});
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
});
Now, run the code and visit http://localhost:3000/login in browser that asks user to login and gives code
in address bar after completing authentication like this:
When I ran http://localhost:3000/book-event in browser, it booked the event in signed-in user's calendar successfully like this:
If you try to book event in different user's calendar, it will give 403 error as access will be denied for users other than signed-in user like this: