{"id":18317914,"url":"https://github.com/idiocc/linkedin","last_synced_at":"2025-04-09T13:51:20.073Z","repository":{"id":57117954,"uuid":"163631021","full_name":"idiocc/linkedin","owner":"idiocc","description":"The LinkedIn OAuth Login Routes For The Idio Web Server.","archived":false,"fork":false,"pushed_at":"2019-01-08T12:52:18.000Z","size":160,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-03-17T03:17:40.441Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://idio.cc","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/idiocc.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-12-31T01:51:32.000Z","updated_at":"2019-01-08T12:52:20.000Z","dependencies_parsed_at":"2022-08-23T03:50:07.737Z","dependency_job_id":null,"html_url":"https://github.com/idiocc/linkedin","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/idiocc%2Flinkedin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/idiocc%2Flinkedin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/idiocc%2Flinkedin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/idiocc%2Flinkedin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/idiocc","download_url":"https://codeload.github.com/idiocc/linkedin/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248054218,"owners_count":21039951,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-05T18:07:51.164Z","updated_at":"2025-04-09T13:51:20.054Z","avatar_url":"https://github.com/idiocc.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# \u003cimg src=\"https://raw.github.com/idiocc/linkedin/master/square.svg?sanitize=true\" align=\"left\"\u003e @idio/linkedin\n\n[![npm version](https://badge.fury.io/js/%40idio%2Flinkedin.svg)](https://npmjs.org/package/@idio/linkedin)\n\n`@idio/linkedin` is The LinkedIn OAuth Login Routes For The Idio Web Server.\n\n```sh\nyarn add -E @idio/linkedin\n```\n\n## Table Of Contents\n\n- [Table Of Contents](#table-of-contents)\n- [API](#api)\n- [`linkedin(router: Router, config: Config)`](#linkedinrouter-routerconfig-config-void)\n  * [`Config`](#type-config)\n  * [finish](#finish)\n  * [error](#error)\n- [`getUser(user: *): User`](#getuseruser--user)\n  * [`User`](#type-user)\n- [`async query(config: QueryConfig): *`](#async-queryconfig-queryconfig-)\n  * [`QueryConfig`](#type-queryconfig)\n- [`linkedInButton(): { idioCommon, style, button }`](#linkedinbutton--idiocommon-style-button-)\n- [Copyright](#copyright)\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\u003cimg src=\".documentary/section-breaks/0.svg?sanitize=true\"\u003e\u003c/a\u003e\u003c/p\u003e\n\n## API\n\nThe package is available by importing its default and named functions:\n\n```js\nimport linkedin, {\n  linkedInButton, query, getUser,\n} from '@idio/linkedin'\n```\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\u003cimg src=\".documentary/section-breaks/1.svg?sanitize=true\"\u003e\u003c/a\u003e\u003c/p\u003e\n\n## `linkedin(`\u003cbr/\u003e\u0026nbsp;\u0026nbsp;`router: Router,`\u003cbr/\u003e\u0026nbsp;\u0026nbsp;`config: Config,`\u003cbr/\u003e`): void`\n\nSets up the `/auth/linkedin` and `/auth/linkedin/redirect` paths on the router to enable LinkedIn App Login. The session middleware needs to be installed to remember the state. The state is destroyed after the redirect.\n\n__\u003ca name=\"type-config\"\u003e`Config`\u003c/a\u003e__: Options for the program.\n\n|        Name        |                       Type                       |                                                                         Description                                                                          |         Default         |\n| ------------------ | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------- |\n| __client_id*__     | _string_                                         | The app's client id.                                                                                                                                         | -                       |\n| __client_secret*__ | _string_                                         | The app's client secret.                                                                                                                                     | -                       |\n| path               | _string_                                         | The server path to start the login flaw and use for redirect (`${path}/redirect`).                                                                           | `/auth/linkedin`        |\n| scope              | _string_                                         | The scope to ask permissions for.                                                                                                                            | `r_liteprofile`         |\n| finish             | _(ctx, token, user) =\u0026gt; {}_                    | The function to complete the authentication that receives the token and the data about the user, such as name and id. The default function redirects to `/`. | `setSession; redirect;` |\n| error              | _(ctx, error, error_description, next) =\u0026gt; {}_ | The function to be called in case of error. If not specified, the middleware will throw an internal server error.                                            | `throw;`                |\n| session            | _Middleware_                                     | The configured session middleware in case the `session` property is not globally available on the context.                                                   | -                       |\n\n```js\nimport linkedIn, { query, linkedInButton, getUser } from '@idio/linkedin'\nimport idioCore from '@idio/core'\n\nconst Server = async () =\u003e {\n  const { url, router, app, middleware: {\n    session,\n  } } = await idioCore({\n    session: {\n      keys: [process.env.SESSION_KEY],\n    },\n    logger: { use: true },\n  }, { port: 0 })\n  router.get('/', async (ctx) =\u003e {\n    const u = await userDiv(ctx.session.user)\n    ctx.body = `\u003c!doctype html\u003e\n    \u003chtml\u003e\n      \u003cbody\u003e\n        ${u}\n        \u003chr\u003e\n        \u0026copy;Art Deco, 2019\n      \u003c/body\u003e\n    \u003c/html\u003e`\n  })\n  router.get('/signout', session, (ctx) =\u003e {\n    ctx.session = null\n    ctx.redirect('/')\n  })\n  linkedIn(router, {\n    session,\n    client_id: process.env.LINKEDIN_ID,\n    client_secret: process.env.LINKEDIN_SECRET,\n    scope: 'r_liteprofile,r_basicprofile',\n    error(ctx, error) {\n      ctx.redirect(`/?error=${error}`)\n    },\n    async finish(ctx, token, user) {\n      const { positions: { values: pos } } = await query({\n        token,\n        path: 'people/~:(positions)',\n        version: 'v1',\n      })\n      const positions = pos.map(({\n        title,\n        company: { id, name },\n        location: { name: location } ,\n      }) =\u003e {\n        return {\n          id, name, title,\n          location: location.replace(/,\\s*$/, ''),\n        }\n      })\n      ctx.session.token = token\n      ctx.session.user = getUser(user)\n      ctx.session.positions = positions\n      ctx.redirect('/')\n    },\n  })\n  app.use(router.routes())\n  return { app, url }\n}\n\nconst userDiv = async (user) =\u003e {\n  if (!user) {\n    const { idioCommon, style, button } = await linkedInButton()\n    return `\n    \u003cstyle\u003e\n      ${idioCommon}\n      ${style}\n    \u003c/style\u003e\n    \u003cdiv class=\"User\"\u003e\n      \u003cp\u003eWelcome.\u003c/p\u003e\n      ${button}\n    \u003c/div\u003e`\n  }\n  const img = `\u003cimg src=\"${user.profilePicture}\" width=\"50\"\u003e`\n  return `\n    \u003cdiv class=\"User\"\u003e\n      ${img} Hello, ${user.firstName} ${user.lastName}!\n      \u003ca href=\"/signout\"\u003eSign out\u003c/a\u003e\n    \u003c/div\u003e`\n}\n```\n```\n[+] LINKEDIN_ID [+] LINKEDIN_SECRET [+] SESSION_KEY \nhttp://localhost:65210 \n  \u003c-- GET /auth/linkedin\n  --\u003e GET /auth/linkedin 302 35ms 487b\n{ body: 'Redirecting to \u003ca href=\"https://www.linkedin.com/oauth/v2/authorization?state=7739\u0026amp;response_type=code\u0026amp;client_id=86986rqg6dmn58\u0026amp;redirect_uri=http%3A%2F%2Flocalhost%3A65210%2Fauth%2Flinkedin%2Fredirect\u0026amp;scope=r_liteprofile%2Cr_basicprofile\"\u003ehttps://www.linkedin.com/oauth/v2/authorization?state=7739\u0026amp;response_type=code\u0026amp;client_id=86986rqg6dmn58\u0026amp;redirect_uri=http%3A%2F%2Flocalhost%3A65210%2Fauth%2Flinkedin%2Fredirect\u0026amp;scope=r_liteprofile%2Cr_basicprofile\u003c/a\u003e.',\n  headers: \n   { location: 'https://www.linkedin.com/oauth/v2/authorization?state=7739\u0026response_type=code\u0026client_id=86986rqg6dmn58\u0026redirect_uri=http%3A%2F%2Flocalhost%3A65210%2Fauth%2Flinkedin%2Fredirect\u0026scope=r_liteprofile%2Cr_basicprofile',\n     'content-type': 'text/html; charset=utf-8',\n     'content-length': '487',\n     'set-cookie': \n      [ 'koa:sess=eyJzdGF0ZSI6NzczOSwiX2V4cGlyZSI6MTU0NzAzODExNTUxOSwiX21heEFnZSI6ODY0MDAwMDB9; path=/; httponly',\n        'koa:sess.sig=w_PIzlf56BzzK4-XTnXWKCD0oMc; path=/; httponly' ],\n     date: 'Tue, 08 Jan 2019 12:48:35 GMT',\n     connection: 'close' },\n  statusCode: 302,\n  statusMessage: 'Found' }\n\n \u003e Redirect to Dialog https://www.linkedin.com/oauth/v2/authorization?state=7739\u0026response_type=code\u0026client_id=86986rqg6dmn58\u0026redirect_uri=http%3A%2F%2Flocalhost%3A65210%2Fauth%2Flinkedin%2Fredirect\u0026scope=r_liteprofile%2Cr_basicprofile\n```\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\u003cimg src=\".documentary/section-breaks/2.svg?sanitize=true\" width=\"15\"\u003e\u003c/a\u003e\u003c/p\u003e\n\n### finish\n\nThe config allows to set the finish function that can be used to alter the logic of setting the token on the session or performing additional operations such as storing a new user in the database. The default sets the token on the `ctx.session` and also sets the user data such as name and id in the `ctx.session.user` property.\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\u003cimg src=\".documentary/section-breaks/3.svg?sanitize=true\" width=\"15\"\u003e\u003c/a\u003e\u003c/p\u003e\n\n### error\n\nThe `error` property of the config represent the function to be called in case of an error such as when the user cancelled the authorisation request. It can be used to redirect to the path and set the error text and description in the query parameters. When default handler is used, the `@idio/linkedin` middleware will throw internally.\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\u003cimg src=\".documentary/section-breaks/4.svg?sanitize=true\"\u003e\u003c/a\u003e\u003c/p\u003e\n\n## `getUser(`\u003cbr/\u003e\u0026nbsp;\u0026nbsp;`user: *,`\u003cbr/\u003e`): User`\n\nWhen data is requested from `/me` route for the lite profile, the results will come back containing a lot of metadata such as names' locales and an array with profile pictures of different sizes. The `getUser` method returns those properties as strings.\n\n__\u003ca name=\"type-user\"\u003e`User`\u003c/a\u003e__: The normalised user data from the `/me` path.\n\n|        Name         |   Type   |           Description           |\n| ------------------- | -------- | ------------------------------- |\n| __id*__             | _string_ | The user ID.                    |\n| __firstName*__      | _string_ | The user's first name.          |\n| __lastName*__       | _string_ | The user's last name.           |\n| __profilePicture*__ | _string_ | The URL to the profile picture. |\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\u003cimg src=\".documentary/section-breaks/5.svg?sanitize=true\"\u003e\u003c/a\u003e\u003c/p\u003e\n\n## `async query(`\u003cbr/\u003e\u0026nbsp;\u0026nbsp;`config: QueryConfig,`\u003cbr/\u003e`): *`\n\nThe query method allows to query the LinkedIn API. The `v2` version of the API only allows to query basic data with the `r_liteprofile` permission. The other methods of the API are not pubic. This package will automatically query the `/me` route to find out the user's name and profile picture, therefore specifying the `r_liteprofile` scope is required. The `v1` version which is used to query positions with the `r_basicprofile` scope will be switched off in March 2019.\n\n__\u003ca name=\"type-queryconfig\"\u003e`QueryConfig`\u003c/a\u003e__: Options for Query.\n\n|    Name    |   Type   |                    Description                    | Default |\n| ---------- | -------- | ------------------------------------------------- | ------- |\n| __token*__ | _string_ | The access token with appropriate permissions.    | -       |\n| __path*__  | _string_ | The API endpoint.                                 | -       |\n| __data*__  | _*_      | The object containing data to query the API with. | -       |\n| version    | _string_ | The version of the API to query.                  | `v2`    |\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\u003cimg src=\".documentary/section-breaks/6.svg?sanitize=true\"\u003e\u003c/a\u003e\u003c/p\u003e\n\n## `linkedInButton(): { idioCommon, style, button }`\n\nThe package provides the implementation of the Sign-In button with CSS and HTML. It was added in favour of the static image button to be able to switch background color on hover, and instead of an SVG button because problems will arise when placing SVG into an `a` element.\n\n|                 Button                  |                                            Source                                            |\n| --------------------------------------- | -------------------------------------------------------------------------------------------- |\n| ![Default Button](img/linkedin.png)     | The default Linked In button from https://developer.linkedin.com/downloads.                  |\n| ![Idio Linkedin Button](img/button.png) | Idio's button CSS+HTML implementation. It supports `hover`, `active` and `focus` properties. |\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\u003cimg src=\".documentary/section-breaks/7.svg?sanitize=true\"\u003e\u003c/a\u003e\u003c/p\u003e\n\n## Copyright\n\n(c) [Idio][1] 2019\n\n[1]: https://idio.cc\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\u003cimg src=\".documentary/section-breaks/-1.svg?sanitize=true\"\u003e\u003c/a\u003e\u003c/p\u003e","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fidiocc%2Flinkedin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fidiocc%2Flinkedin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fidiocc%2Flinkedin/lists"}