Last active
April 4, 2025 11:06
-
-
Save markgarrigan/53104fcf1d0ebd7e260a4efd499b2451 to your computer and use it in GitHub Desktop.
Using axios for azure auth
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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'); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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