LinkedIn authentication strategy for Passport using OAuth 1.0a
npx @tessl/cli install tessl/npm-passport-linkedin@1.0.00
# passport-linkedin
1
2
passport-linkedin is a Passport authentication strategy for integrating LinkedIn OAuth 1.0a authentication into Node.js applications. It allows applications to authenticate users through their LinkedIn accounts with configurable permission scopes and profile field selection.
3
4
## Package Information
5
6
- **Package Name**: passport-linkedin
7
- **Package Type**: npm
8
- **Language**: JavaScript
9
- **Installation**: `npm install passport-linkedin`
10
11
## Core Imports
12
13
```javascript
14
// Default import (recommended)
15
const LinkedInStrategy = require('passport-linkedin');
16
```
17
18
Alternative imports:
19
20
```javascript
21
// Named import
22
const { Strategy } = require('passport-linkedin');
23
24
// Explicit Strategy property access
25
const LinkedInStrategy = require('passport-linkedin').Strategy;
26
```
27
28
## Basic Usage
29
30
```javascript
31
const passport = require('passport');
32
const LinkedInStrategy = require('passport-linkedin');
33
34
passport.use(new LinkedInStrategy({
35
consumerKey: 'YOUR_LINKEDIN_API_KEY',
36
consumerSecret: 'YOUR_LINKEDIN_SECRET_KEY',
37
callbackURL: 'http://localhost:3000/auth/linkedin/callback'
38
},
39
function(token, tokenSecret, profile, done) {
40
// User authentication logic
41
User.findOrCreate({ linkedinId: profile.id }, function (err, user) {
42
return done(err, user);
43
});
44
}
45
));
46
47
// Routes
48
app.get('/auth/linkedin', passport.authenticate('linkedin'));
49
50
app.get('/auth/linkedin/callback',
51
passport.authenticate('linkedin', { failureRedirect: '/login' }),
52
function(req, res) {
53
// Successful authentication
54
res.redirect('/');
55
});
56
```
57
58
## Architecture
59
60
passport-linkedin extends Passport's OAuth 1.0a strategy with LinkedIn-specific customizations:
61
62
- **OAuth1 Foundation**: Built on `passport-oauth1` for standard OAuth 1.0a protocol handling
63
- **LinkedIn API Integration**: Configured with LinkedIn-specific endpoints and request modifications
64
- **Scope Handling**: Custom implementation to handle LinkedIn's URL parameter-based scope requests
65
- **Profile Normalization**: Converts LinkedIn API responses to standardized Passport profile format
66
- **Field Mapping**: Flexible system for requesting specific LinkedIn profile fields
67
- **Error Handling**: LinkedIn-specific error detection and OAuth error wrapping
68
69
## Capabilities
70
71
### LinkedIn Strategy
72
73
Creates a new LinkedIn authentication strategy for Passport.
74
75
```javascript { .api }
76
/**
77
* LinkedIn authentication strategy constructor
78
* @param {Object} options - Configuration options
79
* @param {Function} verify - Verification callback function
80
*/
81
function Strategy(options, verify);
82
```
83
84
**Options:**
85
86
```javascript { .api }
87
interface StrategyOptions {
88
/** LinkedIn API consumer key (required) */
89
consumerKey: string;
90
/** LinkedIn API consumer secret (required) */
91
consumerSecret: string;
92
/** OAuth callback URL (required) */
93
callbackURL: string;
94
/** Specific profile fields to request (optional) */
95
profileFields?: string[];
96
/** Request token URL (optional) */
97
requestTokenURL?: string;
98
/** Access token URL (optional) */
99
accessTokenURL?: string;
100
/** User authorization URL (optional) */
101
userAuthorizationURL?: string;
102
/** Session key for OAuth tokens (optional) */
103
sessionKey?: string;
104
}
105
```
106
107
**Verify Callback:**
108
109
```javascript { .api }
110
/**
111
* Verification callback function
112
* @param {string} token - OAuth access token
113
* @param {string} tokenSecret - OAuth token secret
114
* @param {Object} profile - User profile object
115
* @param {Function} done - Completion callback
116
*/
117
function verify(token, tokenSecret, profile, done);
118
```
119
120
### Extended Permissions
121
122
Request extended permissions using the scope parameter during authentication.
123
124
```javascript
125
// Single scope
126
app.get('/auth/linkedin',
127
passport.authenticate('linkedin', { scope: 'r_fullprofile' }));
128
129
// Multiple scopes
130
app.get('/auth/linkedin',
131
passport.authenticate('linkedin', {
132
scope: ['r_basicprofile', 'r_emailaddress']
133
}));
134
```
135
136
### Profile Field Selection
137
138
Configure specific profile fields to request from LinkedIn API.
139
140
```javascript
141
passport.use(new LinkedInStrategy({
142
consumerKey: 'YOUR_API_KEY',
143
consumerSecret: 'YOUR_SECRET',
144
callbackURL: 'http://localhost:3000/auth/linkedin/callback',
145
profileFields: ['id', 'first-name', 'last-name', 'email-address', 'headline']
146
},
147
function(token, tokenSecret, profile, done) {
148
// Handle authentication
149
return done(null, profile);
150
}
151
));
152
```
153
154
### Strategy Methods
155
156
#### authenticate
157
158
Handles the OAuth authentication flow and processes LinkedIn responses. Extends the base OAuth1 strategy authentication with LinkedIn-specific error handling.
159
160
```javascript { .api }
161
/**
162
* Authenticate request using LinkedIn OAuth
163
* @param {Object} req - HTTP request object
164
* @param {Object} options - Authentication options (scope, etc.)
165
* @returns {void} - Calls success(), fail(), or error() on completion
166
*/
167
Strategy.prototype.authenticate(req, options);
168
```
169
170
**Special behavior:**
171
- Detects `oauth_problem=user_refused` query parameter and calls `fail()` for user denial
172
- Delegates to parent OAuth1Strategy for standard OAuth flow
173
- Handles LinkedIn-specific OAuth token request modifications for scope parameter
174
175
#### userProfile
176
177
Retrieves user profile information from LinkedIn API.
178
179
```javascript { .api }
180
/**
181
* Retrieve user profile from LinkedIn
182
* @param {string} token - OAuth access token
183
* @param {string} tokenSecret - OAuth token secret
184
* @param {Object} params - Additional parameters
185
* @param {Function} done - Completion callback with (err, profile)
186
*/
187
Strategy.prototype.userProfile(token, tokenSecret, params, done);
188
```
189
190
#### requestTokenParams
191
192
Returns LinkedIn-specific parameters for request token requests, with special handling for scope parameters.
193
194
```javascript { .api }
195
/**
196
* Return extra LinkedIn-specific parameters for request token request
197
* @param {Object} options - Options containing scope information
198
* @param {string|string[]} options.scope - LinkedIn permission scope(s)
199
* @returns {Object} Parameters object with scope handling
200
*/
201
Strategy.prototype.requestTokenParams(options);
202
```
203
204
**Scope handling:**
205
- String scopes are passed directly: `'r_basicprofile'`
206
- Array scopes are joined with '+': `['r_basicprofile', 'r_emailaddress']` → `'r_basicprofile+r_emailaddress'`
207
- LinkedIn expects scope as URL query parameter, not in POST body (handled automatically)
208
209
## Types
210
211
### Profile Object
212
213
The profile object returned by LinkedIn contains normalized user information:
214
215
```javascript { .api }
216
interface LinkedInProfile {
217
/** Always 'linkedin' */
218
provider: string;
219
/** LinkedIn user ID */
220
id: string;
221
/** Full display name (firstName + lastName) */
222
displayName: string;
223
/** Name breakdown */
224
name: {
225
/** Last name */
226
familyName: string;
227
/** First name */
228
givenName: string;
229
};
230
/** Email addresses (if requested via profileFields) */
231
emails?: Array<{
232
/** Email address value */
233
value: string;
234
}>;
235
/** Raw API response body */
236
_raw: string;
237
/** Parsed JSON response */
238
_json: {
239
/** LinkedIn user ID */
240
id: string;
241
/** First name from LinkedIn */
242
firstName: string;
243
/** Last name from LinkedIn */
244
lastName: string;
245
/** Email address (if requested) */
246
emailAddress?: string;
247
/** Additional fields based on profileFields configuration */
248
[key: string]: any;
249
};
250
}
251
```
252
253
### Strategy Instance Properties
254
255
```javascript { .api }
256
interface StrategyInstance {
257
/** Strategy name, always 'linkedin' */
258
name: string;
259
}
260
```
261
262
## Profile Field Mapping
263
264
Standard profile field names are mapped to LinkedIn API fields:
265
266
- `'id'` → `'id'`
267
- `'name'` → `['first-name', 'last-name']`
268
- `'emails'` → `'email-address'`
269
270
Unmapped field names are passed directly to the LinkedIn API, allowing access to additional LinkedIn profile data.
271
272
### Profile Field Conversion
273
274
The strategy internally converts profile field names using a mapping system:
275
276
```javascript { .api }
277
/**
278
* Internal method to convert profile field names to LinkedIn API format
279
* @param {string[]} profileFields - Array of profile field names
280
* @returns {string} Comma-separated LinkedIn API field names
281
* @private
282
*/
283
Strategy.prototype._convertProfileFields(profileFields);
284
```
285
286
**Example conversions:**
287
- `['id', 'name', 'emails']` → `'id,first-name,last-name,email-address'`
288
- `['id', 'headline', 'industry']` → `'id,headline,industry'` (unmapped fields passed through)
289
290
This enables flexible profile data collection while maintaining compatibility with standard field names.
291
292
## Error Handling
293
294
The strategy handles common error scenarios:
295
296
- **User Denial**: When users deny authorization, the strategy detects `oauth_problem=user_refused` parameter and calls `fail()`
297
- **Profile Fetch Errors**: API errors during profile retrieval are wrapped in `InternalOAuthError` from passport-oauth1
298
- **Invalid Responses**: JSON parsing errors during profile processing are passed to the done callback
299
300
### InternalOAuthError
301
302
```javascript { .api }
303
/**
304
* OAuth-specific error wrapper from passport-oauth1
305
* @param {string} message - Error description
306
* @param {Error} err - Original error object
307
*/
308
class InternalOAuthError extends Error {
309
constructor(message, err);
310
/** OAuth error details */
311
oauthError: Error;
312
}
313
```
314
315
## Scope Values
316
317
Common LinkedIn permission scopes:
318
319
- `r_basicprofile` - Basic profile information
320
- `r_emailaddress` - Email address
321
- `r_fullprofile` - Full profile access
322
- `w_messages` - Send messages
323
- `rw_company_admin` - Company administration
324
325
Multiple scopes are joined with '+' when sent to LinkedIn API.