Created
August 6, 2024 11:25
-
-
Save follow-prince/0e531c2540faa310c03e98b33a195be4 to your computer and use it in GitHub Desktop.
In this blog post, we'll walk through the process of integrating LinkedIn OAuth authentication in a React frontend with an Express.js backend. This guide will cover setting up the backend to handle LinkedIn's OAuth flow, creating a simple React frontend for user interaction, and handling the authentication callback to fetch user information.
This file contains 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
// Next, we'll create a React frontend that handles user interaction, initiates the LinkedIn login process, and displays the user's profile information. | |
### Frontend Setup: | |
import React, { useState } from 'react'; | |
import axios from 'axios'; | |
const clientId = 'YOUR_LINKEDIN_CLIENT_ID'; | |
const redirectUri = 'http://localhost:3000/linkedin/callback'; | |
const App = () => { | |
const [userInfo, setUserInfo] = useState(null); | |
const [error, setError] = useState(null); | |
const handleLogin = () => { | |
const linkedinAuthUrl = `https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=${clientId}&redirect_uri=${encodeURIComponent(redirectUri)}&scope=r_liteprofile%20r_emailaddress`; | |
window.location.href = linkedinAuthUrl; | |
}; | |
const handleCallback = async () => { | |
const queryParams = new URLSearchParams(window.location.search); | |
const code = queryParams.get('code'); | |
if (code) { | |
try { | |
const response = await axios.post('http://localhost:3000/linkedin/callback', { code }); | |
setUserInfo(response.data); | |
} catch (err) { | |
setError('Error fetching user details'); | |
} | |
} | |
}; | |
React.useEffect(() => { | |
if (window.location.pathname === '/linkedin/callback') { | |
handleCallback(); | |
} | |
}, []); | |
return ( | |
<div> | |
<h1>LinkedIn OAuth Example</h1> | |
{!userInfo ? ( | |
<button onClick={handleLogin}>Login with LinkedIn</button> | |
) : ( | |
<div> | |
<h2>User Information</h2> | |
<p>Name: {userInfo.localizedFirstName} {userInfo.localizedLastName}</p> | |
<p>Profile Picture: <img src={userInfo.profilePicture.displayImage~.elements[0].identifiers[0].identifier} alt="Profile" width="100" /></p> | |
</div> | |
)} | |
{error && <p>{error}</p>} | |
</div> | |
); | |
}; | |
export default App; | |
### Key Points: | |
// - Initiating Login: The `handleLogin` function redirects the user to LinkedIn's authorization URL with the required parameters. | |
// - Handling Callback: The `handleCallback` function extracts the authorization code from the URL, sends it to the backend, and retrieves the user's information. | |
// - Displaying User Info: Once the user's data is received, it displays their name and profile picture. |
This file contains 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
// In this blog post, we'll walk through the process of integrating LinkedIn OAuth authentication in a React frontend with an Express.js backend. This guide will cover setting up the backend to handle LinkedIn's OAuth flow, creating a simple React frontend for user interaction, and handling the authentication callback to fetch user information. | |
## 1. Setting Up the Express.js Backend | |
// We'll start by setting up the backend server using Express.js. The backend will handle the OAuth flow, including exchanging the authorization code for access and ID tokens and verifying these tokens. | |
### Backend Setup: | |
const express = require('express'); | |
const axios = require('axios'); | |
const querystring = require('querystring'); | |
const jwt = require('jsonwebtoken'); | |
const jwksClient = require('jwks-rsa'); | |
const bodyParser = require('body-parser'); | |
const cors = require('cors'); | |
const app = express(); | |
const clientId = 'YOUR_LINKEDIN_CLIENT_ID'; | |
const clientSecret = 'YOUR_LINKEDIN_CLIENT_SECRET'; | |
const redirectUri = 'http://localhost:3000/linkedin/callback'; | |
const jwksUri = 'https://www.linkedin.com/oauth/v2/jwks'; | |
const issuer = 'https://www.linkedin.com'; | |
app.use(cors()); | |
app.use(bodyParser.json()); | |
### Key Points: | |
// - Dependencies: We use `axios` for HTTP requests, `querystring` for encoding URL parameters, `jsonwebtoken` and `jwks-rsa` for token handling, and `cors` and `bodyParser` for middleware. | |
// - OAuth Configuration: Set up your LinkedIn Client ID, Client Secret, and Redirect URI. | |
### Handling LinkedIn Callback: | |
app.post('/linkedin/callback', async (req, res) => { | |
const code = req.body.code; | |
if (!code) { | |
return res.status(400).json({ error: 'Authorization code not provided' }); | |
} | |
try { | |
const tokenResponse = await axios.post( | |
'https://www.linkedin.com/oauth/v2/accessToken', | |
querystring.stringify({ | |
grant_type: 'authorization_code', | |
code, | |
redirect_uri: redirectUri, | |
client_id: clientId, | |
client_secret: clientSecret, | |
}), | |
{ headers: { 'Content-Type': 'application/x-www-form-urlencoded' } } | |
); | |
const { access_token: accessToken, id_token: idToken } = tokenResponse.data; | |
if (!accessToken || !idToken) { | |
throw new Error('Failed to obtain access token or ID token'); | |
} | |
const decodedToken = jwt.decode(idToken, { complete: true }); | |
if (!decodedToken || !decodedToken.header || !decodedToken.header.kid) { | |
throw new Error('Invalid ID token structure'); | |
} | |
const { kid } = decodedToken.header; | |
const client = jwksClient({ jwksUri }); | |
client.getSigningKey(kid, async (err, key) => { | |
if (err) { | |
return res.status(500).json({ error: 'Error retrieving signing key' }); | |
} | |
const signingKey = key.getPublicKey(); | |
jwt.verify(idToken, signingKey, { algorithms: ['RS256'], issuer, audience: clientId }, async (err, decoded) => { | |
if (err) { | |
return res.status(500).json({ error: `ID token verification failed: ${err.message}` }); | |
} | |
try { | |
const userInfoResponse = await axios.get('https://api.linkedin.com/v2/me', { | |
headers: { Authorization: `Bearer ${accessToken}` }, | |
}); | |
res.json(userInfoResponse.data); | |
} catch (error) { | |
console.error('Error fetching user details:', error.message); | |
res.status(500).json({ error: 'Failed to fetch user details' }); | |
} | |
}); | |
}); | |
} catch (error) { | |
console.error('Error during LinkedIn OAuth flow:', error.message); | |
let errorMessage = 'Internal Server Error'; | |
if (error.response) { | |
errorMessage = `LinkedIn API error: ${error.response.data.error_description || error.response.data.message}`; | |
} else if (error.request) { | |
errorMessage = 'No response received from LinkedIn API'; | |
} else { | |
errorMessage = `Error in request setup: ${error.message}`; | |
} | |
res.status(500).json({ error: errorMessage }); | |
} | |
}); | |
app.listen(3000, () => { | |
console.log('Server running on http://localhost:3000'); | |
}); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment