deno-linkedin
An attempt to port the LinkedIn API to Deno
Summary of features
- OAuth2
- Share posts on LinkedIn with images, videos, articles, and documents
- Get user profile information
- Post comments on posts
- Get uploaded media or assets
- Post comments containing media (images, videos, articles, and documents)
- Get userâs connections
- Get userâs companies
- Get userâs groups
- Get userâs posts
- Get userâs comments
- Get userâs likes
- Get userâs shares
⌠Some other stuff because this API is super big.
Usage
For more usage examples see an example repository
Youâll need:
- A LinkedIn Developer account (go to https://developer.linkedin.com/)
- Your Client ID and Secret
- A redirect URL
- A list of scopes
Import the library:
import { LinkedinClient } from 'https://deno.land/x/linkedin/mod.ts'
Create a new client:
const ln = new LinkedinClient(options)
Where options
is an object with the following interface:
interface LinkedinClientOptions {
clientId: string // Your client ID
clientSecret: string // Your client secret
oauthCallbackUrl: string // Your redirect URL
oauthScopes?: string[] // The scopes you want to request if not passed will default to r_emailaddress and r_basicprofile
customLogger?: typeof logger // A custom logger if you want to, this is an instance of https://deno.land/std@0.212.0/log
retryAttempts?: number // Number of retry attempts if a request fails (default 3)
defaultDelayBetweenRequestsMs?: number // Default delay between retry requests in milliseconds (default 500)
noRetries?: boolean // If true, no retries will be attempted (default false)
}
About Retries: The LinkedIn API sometimes take a while to process images, videos or other data. If this is the case, posts cannot be created unless the data is processed. This is a retry mechanism that will retry things like uploading videos, posting comments, etc with an exponential backoff of
delay * retry
milliseconds. This is a best effort approach and itâs not guaranteed to work.
Logging in
This lib only provides the 3-legged OAuth2 flow. Youâll need to implement your own server to handle the callback. The callback will receive a code
query parameter that youâll need to pass to the exchangeLoginToken
method.
import { LinkedinClient, InvalidStateError } from 'https://deno.land/x/linkedin/mod.ts'
const ln = new LinkedinClient(options) // fill in the options with the callback url
let authenticatedClient = undefined
// This is your server, let's say it's running oak
const app = new Application()
// This is the URL you'll need to redirect the user to
app.get('/oauth/login', (ctx) => {
const loginObject = ln.loginUrl // this returns an object with a url and a nonce
ctx.response.headers.set('Location', loginObject.url)
ctx.response.status = 302
return
})
// this is the handler for the callback
app.get('/oauth/callback', async (ctx) => {
const params = ctx.request.url.searchParams
if (params.has('error')) {
throw oak.createHttpError(400, 'Error from Linkedin', {
cause: {
error: params.get('error'),
error_description: decodeURIComponent(params.get('error_description')!)
}
})
}
const code = params.get('code')
const state = params.get('state')
if (!code || !state) {
throw oak.createHttpError(422, 'Missing code or state')
}
try {
authenticatedClient = await ln.exchangeLoginToken(code, state) // you can use this to make requests to the API
ctx.response.status = 200
ctx.response.body = {
status: 'ok',
message: 'Logged in successfully'
}
} catch (err) {
if (err instanceof InvalidStateError) {
ctx.response.status = 401
ctx.response.body = {
status: 'error',
message: 'Invalid state'
}
return
}
}
})
Nonce, state and CSRF
The loginUrl
method will return an object with a url
and a nonce
. The nonce
is a random string that youâll need to store in your session. When the user is redirected to the callback URL, youâll need to check that the state
query parameter matches the nonce
you stored in your session. This is to prevent CSRF attacks.
This lib has a built-in simple session manager that will save all generated nonces in memory for a specified amount of time (defined in the LinkedinClientOptions
object). The session manager lives in a static property of the LinkedinClient
class called validSessions
, itâs a read-only Set
of strings that contains all the valid nonces that were generated.
When the exchangeLoginToken
method is called, it will check that the state
parameter matches one of the nonces in the validSessions
set. If it doesnât, it will throw an InvalidStateError
error and early return.
You can disable this behavior by setting the noValidateCSRF
option to true
in the LinkedinClientOptions
object. By doing this, the nonces will no longer be saved, and the exchangeLoginToken
method will not check the state
parameter and will just exchange the code for an access token. Only do this if you intend to implement your own nonce validation mechanism.
Making requests
Token handling
The exchange method will return an authenticated client that you can use to make requests to the API. This client contains all the methods you need to make requests to the API (at least the available ones).
It will store both the access token and the refresh token in memory. You can access them through the accessToken
and refreshToken
properties. These accessors will make checks to see if the tokens are expired but wonât refresh them automatically. You can use the refreshAccessToken
method to refresh all the tokens.
In case one of the tokens is expired or not present, the lib will throw an error. You can catch this error and call refreshAccessToken
to refresh the tokens and retry the request, except if the refresh token itself is expired, in this case you need to log in again since thereâs no way to refresh the refresh token.