Skip to content

Instantly share code, notes, and snippets.

@markgarrigan
Last active April 4, 2025 11:06
Show Gist options
  • Save markgarrigan/53104fcf1d0ebd7e260a4efd499b2451 to your computer and use it in GitHub Desktop.
Save markgarrigan/53104fcf1d0ebd7e260a4efd499b2451 to your computer and use it in GitHub Desktop.
Using axios for azure auth
app.get('/auth/microsoft', (req, res) => {
const redirectUri = 'http://localhost:3000/auth/microsoft/callback'; // This must match the app registration
const finalRedirect = req.query.redirect_uri || '/';
req.session.redirectUri = finalRedirect;
const params = new URLSearchParams({
client_id: process.env.MICROSOFT_CLIENT_ID,
response_type: 'code',
redirect_uri: redirectUri,
scope: 'openid profile offline_access https://graph.microsoft.com/.default',
prompt: 'select_account',
response_mode: 'query', // 👈 important: ensures Microsoft returns the code in the query string
});
const authorizationUrl = `https://login.microsoftonline.com/common/oauth2/v2.0/authorize?${params.toString()}`;
res.redirect(authorizationUrl);
});
app.get('/auth/microsoft/callback', async (req, res) => {
const code = req.query.code;
const redirectUri = 'http://localhost:3000/auth/microsoft/callback';
const tokenEndpoint = 'https://login.microsoftonline.com/common/oauth2/v2.0/token';
const tokenPayload = {
client_id: process.env.MICROSOFT_CLIENT_ID,
client_secret: process.env.MICROSOFT_CLIENT_SECRET,
grant_type: 'authorization_code',
code,
redirect_uri: redirectUri,
scope: 'https://graph.microsoft.com/.default offline_access openid profile',
};
const { data: graphToken } = await axios.post(tokenEndpoint, qs.stringify(tokenPayload), {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
});
// Step 3: Use refresh_token to get Azure token
const azurePayload = {
client_id: process.env.MICROSOFT_CLIENT_ID,
client_secret: process.env.MICROSOFT_CLIENT_SECRET,
grant_type: 'refresh_token',
refresh_token: graphToken.refresh_token,
scope: 'https://management.azure.com/.default offline_access openid profile',
};
const { data: azureToken } = await axios.post(tokenEndpoint, qs.stringify(azurePayload), {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
});
const profileResponse = await axios.get('https://graph.microsoft.com/v1.0/me', {
headers: {
Authorization: `Bearer ${graphToken.access_token}`,
},
});
// Step 4: Store in session
req.session.tokens = {
graph: {
accessToken: graphToken.access_token,
refreshToken: graphToken.refresh_token,
},
azure: {
accessToken: azureToken.access_token,
refreshToken: azureToken.refresh_token,
},
};
req.session.user = profileResponse.data;
// Step 5: Redirect to original destination
const finalRedirect = req.session.redirectUri || '/';
delete req.session.redirectUri;
req.session.save(() => res.redirect(finalRedirect));
});
function requireAuth(req, res, next) {
if (req.session?.user && req.session?.tokens?.graph?.accessToken) {
req.user = req.session.user;
return next();
}
res.status(401).send('Unauthorized');
}
function toFormData(obj = {}, parentKey = '') {
const params = new URLSearchParams();
function appendFormValue(key, value) {
if (value === undefined || value === null) return;
if (Array.isArray(value)) {
for (const item of value) {
appendFormValue(key, item);
}
} else if (typeof value === 'object' && !(value instanceof Date)) {
for (const [nestedKey, nestedValue] of Object.entries(value)) {
appendFormValue(`${key}.${nestedKey}`, nestedValue);
}
} else {
params.append(key, value.toString());
}
}
for (const [key, value] of Object.entries(obj)) {
const fullKey = parentKey ? `${parentKey}.${key}` : key;
appendFormValue(fullKey, value);
}
return params;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment