import { Tabs, Callout, Steps } from "nextra/components";
# LinkedIn
The LinkedIn auth provider enables tools and agents to call the LinkedIn API on behalf of a user.
### What's documented here
This page describes how to use and configure LinkedIn auth with Arcade.
This auth provider is used by:
- Your [app code](#using-linkedin-auth-in-app-code) that needs to call LinkedIn APIs
- Or, your [custom tools](#using-linkedin-auth-in-custom-tools) that need to call LinkedIn APIs
## Configuring LinkedIn auth
When using your own app credentials, make sure you configure your project to
use a [custom user
verifier](/guides/user-facing-agents/secure-auth-production#build-a-custom-user-verifier).
Without this, your end-users will not be able to use your app or agent in
production.
In a production environment, you will most likely want to use your own LinkedIn app credentials. This way, your users will see your application's name requesting permission.
Before showing how to configure your LinkedIn app credentials, let's go through the steps to create a LinkedIn app.
### Create a LinkedIn app
- Follow LinkedIn's guide to [setting up user authorization](https://learn.microsoft.com/en-us/linkedin/shared/authentication/authentication)
- On the Products tab, add the products you need for your app (e.g. "Share on LinkedIn")
- At a minimum, you _must_ add the "Sign In with LinkedIn using OpenID Connect" product
- On the Auth tab, set the redirect URL to the redirect URL generated by Arcade (see below)
- Copy the client ID and client secret to use below
Next, add the LinkedIn app to Arcade.
## Configuring your own LinkedIn Auth Provider in Arcade
## Using LinkedIn auth in app code
Use the LinkedIn auth provider in your own agents and AI apps to get a user token for LinkedIn APIs. See [authorizing agents with Arcade](/get-started/about-arcade) to understand how this works.
Use `client.auth.start()` to get a user token for LinkedIn APIs:
```python {23-27}
import requests
from arcadepy import Arcade
client = Arcade() # Automatically finds the `ARCADE_API_KEY` env variable
user_id = "{arcade_user_id}"
"""
In this example, we will use Arcade to authenticate with LinkedIn and post a
message to the user's LinkedIn feed.
There is a tool for that in the Arcade SDK, which simplifies the process for
you to post messages to the user's LinkedIn feed either through our Python or
JavaScript SDKs or via LLM tool calling.
Below we are just showing how to use Arcade as an auth provider, if you ever
need to.
"""
# Start the authorization process
auth_response = client.auth.start(
user_id=user_id,
provider="linkedin",
scopes=["w_member_social"],
)
if auth_response.status != "completed":
print("Please complete the authorization challenge in your browser:")
print(auth_response.url)
# Wait for the authorization to complete
auth_response = client.auth.wait_for_completion(auth_response)
if not auth_response.context.token:
raise ValueError("No token found in auth response")
token = auth_response.context.token
user_id = (
None
if not auth_response.context.authorization
else auth_response.context.authorization.user_info.get("sub")
)
if not user_id:
raise ValueError("User ID not found.")
# Prepare the payload data for the LinkedIn API
message = "Hello, from Arcade.dev!"
payload = {
"author": f"urn:li:person:{user_id}",
"lifecycleState": "PUBLISHED",
"specificContent": {
"com.linkedin.ugc.ShareContent": {
"shareCommentary": {"text": message},
"shareMediaCategory": "NONE",
}
},
"visibility": {"com.linkedin.ugc.MemberNetworkVisibility": "PUBLIC"},
}
response = requests.post(
"https://api.linkedin.com/v2/ugcPosts",
headers={"Authorization": f"Bearer {token}"},
json=payload,
)
response.raise_for_status()
print(response.json())
```
```javascript {20-22}
import { Arcade } from "@arcadeai/arcadejs";
const client = new Arcade(); // Automatically finds the `ARCADE_API_KEY` env variable
const userId = "{arcade_user_id}";
/*
In this example, we will use Arcade to authenticate with LinkedIn and post a
message to the user's LinkedIn feed.
There is a tool for that in the Arcade SDK, which simplifies the process for
you to post messages to the user's LinkedIn feed either through our Python or
JavaScript SDKs or via LLM tool calling.
Below we are just showing how to use Arcade as an auth provider, if you ever
need to.
*/
// Start the authorization process
let authResponse = await client.auth.start(userId, "linkedin", {
scopes: ["w_member_social"],
});
if (authResponse.status !== "completed") {
console.log("Please complete the authorization challenge in your browser:");
console.log(authResponse.url);
}
// Wait for the authorization to complete
authResponse = await client.auth.waitForCompletion(authResponse);
if (!authResponse.context.token) {
throw new Error("No token found in auth response");
}
const token = authResponse.context.token;
const linkedInUserId = authResponse.context.authorization?.user_info?.sub;
if (!linkedInUserId) {
throw new Error("User ID not found.");
}
// Prepare the payload data for the LinkedIn API
const message = "Hello, from Arcade.dev!";
const payload = {
author: `urn:li:person:${linkedInUserId}`,
lifecycleState: "PUBLISHED",
specificContent: {
"com.linkedin.ugc.ShareContent": {
shareCommentary: { text: message },
shareMediaCategory: "NONE",
},
},
visibility: { "com.linkedin.ugc.MemberNetworkVisibility": "PUBLIC" },
};
const response = await fetch("https://api.linkedin.com/v2/ugcPosts", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log(data);
```
## Using LinkedIn auth in custom tools
You can author your own [custom tools](/guides/create-tools/tool-basics/build-mcp-server) that interact with LinkedIn APIs.
Use the `LinkedIn()` auth class to specify that a tool requires authorization with LinkedIn. The `context.authorization.token` field will be automatically populated with the user's LinkedIn token:
```python {6-7,10-14,33}
from typing import Annotated
import httpx
from arcade_tdk.errors import ToolExecutionError
from arcade_tdk import ToolContext, tool
from arcade_tdk.auth import LinkedIn
@tool(
requires_auth=LinkedIn(
scopes=["w_member_social"],
)
)
async def create_text_post(
context: ToolContext,
text: Annotated[str, "The text content of the post"],
) -> Annotated[str, "URL of the shared post"]:
"""Share a new text post to LinkedIn."""
endpoint = "/ugcPosts"
# The LinkedIn user ID is required to create a post, even though we're using the user's access token.
# Arcade Engine gets the current user's info from LinkedIn and automatically populates context.authorization.user_info.
# LinkedIn calls the user ID "sub" in their user_info data payload. See:
# https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin-v2#api-request-to-retreive-member-details
user_id = context.authorization.user_info.get("sub")
if not user_id:
raise ToolExecutionError(
"User ID not found.",
developer_message="User ID not found in `context.authorization.user_info.sub`",
)
headers = {"Authorization": f"Bearer {context.authorization.token}"}
author_id = f"urn:li:person:{user_id}"
payload = {
"author": author_id,
"lifecycleState": "PUBLISHED",
"specificContent": {
"com.linkedin.ugc.ShareContent": {
"shareCommentary": {"text": text},
"shareMediaCategory": "NONE",
}
},
"visibility": {"com.linkedin.ugc.MemberNetworkVisibility": "PUBLIC"},
}
async with httpx.AsyncClient() as client:
response = await client.post(
url=f"https://api.linkedin.com/v2/{endpoint}",
headers=headers,
json=payload,
)
response.raise_for_status()
share_id = response.json().get("id")
return f"https://www.linkedin.com/feed/update/{share_id}/"
```