Skip to content

Instantly share code, notes, and snippets.

@follow-prince
Created August 6, 2024 11:25
Show Gist options
  • Save follow-prince/0e531c2540faa310c03e98b33a195be4 to your computer and use it in GitHub Desktop.
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.
// 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.
// 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