linkedin_oauth2 1.0.0
linkedin_oauth2: ^1.0.0 copied to clipboard
A clean, lightweight Flutter package for secure LinkedIn OAuth 2.0 authentication and user profile retrieval using OpenID Connect.
LinkedIn OAuth2 Package #
A secure, clean, and lightweight Flutter package for implementing LinkedIn OAuth 2.0 authentication and user profile retrieval using OpenID Connect (OIDC).
Features #
- Secure OAuth 2.0 flow: Initiates authentication using secure native web browser sessions.
- OpenID Connect (OIDC): Fetches authentic profile data (name, email, profile picture) using the official LinkedIn userinfo endpoint.
- Easy integration: Features a ready-to-use branded LinkedIn sign-in button.
- Comprehensive error handling: Built-in exception types for cancelled, failed, network, and missing code scenarios.
- Standard compliant: Fully respects custom schemes, deep linking, and OAuth specifications.
Installation #
Add linkedin_oauth2 to your pubspec.yaml:
dependencies:
linkedin_oauth2: ^0.0.1
Run the package configuration command:
flutter pub get
LinkedIn Developer Portal Setup #
- Go to the LinkedIn Developer Portal.
- Create your application and obtain your Client ID.
- Under the "Products" tab, request access to "Sign In with LinkedIn using OpenID Connect". This enables OIDC scopes (openid, profile, email).
- Under the "Auth" tab, configure your "Authorized Redirect URLs".
- Note: LinkedIn only accepts secure HTTPS URLs (e.g.
https://yourdomain.com/callbackorhttps://yourbackend.com/auth/linkedin/callback). It does not accept custom schemes likemyapp://callback.
- Note: LinkedIn only accepts secure HTTPS URLs (e.g.
Handling Redirect URIs on Mobile #
Because the LinkedIn Developer Portal only allows standard HTTPS redirect URIs, mobile applications must handle the redirect using one of two options:
Option A: App Links / Universal Links (Direct Deep Linking) #
Configure your mobile app to register and intercept an HTTPS redirect domain.
- Register your HTTPS callback URL (e.g.,
https://yourdomain.com/callback) in the LinkedIn Developer Portal. - Setup Android App Links and iOS Universal Links for
yourdomain.com. - Configure your app to listen to redirects targeting this URL. Control will be returned to the app upon loading the URL.
Android Setup for Option A
Add the intent filter to android/app/src/main/AndroidManifest.xml:
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="yourdomain.com" android:pathPrefix="/callback" />
</intent-filter>
Host your verification file at https://yourdomain.com/.well-known/assetlinks.json.
iOS Setup for Option A
- Enable the Associated Domains capability in Xcode.
- Add the domain entry:
applinks:yourdomain.com. - Host the verification file at
https://yourdomain.com/.well-known/apple-app-site-association.
Dart Configuration
final signIn = LinkedInSignIn(
clientId: 'YOUR_CLIENT_ID',
redirectUri: 'https://yourdomain.com/callback',
customScheme: 'https',
);
Option B: Backend Redirection Bridge (Recommended for Simplicity) #
Avoid setting up Universal/App Links by routing the redirect through a backend server endpoint which redirects back to the mobile app's custom URL scheme.
- Register your backend endpoint (e.g.,
https://api.yourbackend.com/auth/linkedin/callback) in the LinkedIn Developer Portal. - When LinkedIn redirects the browser to your backend, your server responds with an HTTP 302 redirect pointing to your app's custom scheme URL (e.g.,
myapp://callback?code=CODE&state=STATE). - The mobile OS intercepts the custom scheme and returns control to your Flutter app.
Android Setup for Option B
Register your custom scheme in android/app/build.gradle:
android {
defaultConfig {
manifestPlaceholders = [
appAuthRedirectScheme: 'myapp'
]
}
}
iOS Setup for Option B
Add the custom URL scheme to ios/Runner/Info.plist:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
</dict>
</array>
Backend Redirection Code Example (Node.js / Express)
app.get('/auth/linkedin/callback', (req, res) => {
const code = req.query.code;
const state = req.query.state;
if (!code) {
return res.status(400).send('Authorization code missing');
}
// Issue 302 redirect back to the mobile app's custom URI scheme
res.redirect(`myapp://callback?code=${code}&state=${state || ''}`);
});
Dart Configuration
final signIn = LinkedInSignIn(
clientId: 'YOUR_CLIENT_ID',
redirectUri: 'https://api.yourbackend.com/auth/linkedin/callback',
customScheme: 'myapp',
);
Usage Examples #
1. Branded Sign-in Button #
Include the pre-styled LinkedInSignInButton in your UI:
LinkedInSignInButton(
onPressed: () {
// Trigger login flow
executeLinkedInLogin();
},
)
2. Full Authentication Flow #
Perform authorization and profile retrieval:
import 'package:linkedin_oauth2/linkedin_oauth2.dart';
final signIn = LinkedInSignIn(
clientId: 'YOUR_CLIENT_ID',
redirectUri: 'https://api.yourbackend.com/auth/linkedin/callback', // Backend Redirect Bridge
customScheme: 'myapp',
);
Future<void> executeLinkedInLogin() async {
try {
// 1. Get Authorization Code
final authResult = await signIn.getAuthCode(
scopes: ['openid', 'profile', 'email'],
state: 'secure_random_state_string',
);
print('Authorization code received: ${authResult.code}');
// 2. Exchange authorization code for an Access Token on your backend
final accessToken = await exchangeCodeWithBackend(authResult.code);
// 3. Retrieve User Profile Details
final profile = await signIn.getUserProfile(accessToken);
print('Hello, ${profile.name}');
print('Email: ${profile.email}');
print('Picture URL: ${profile.picture}');
} on LinkedInAuthCancelledException {
print('User cancelled the sign in flow');
} on LinkedInAuthFailedException catch (e) {
print('Authentication failed: ${e.message}');
} on LinkedInAuthNetworkException {
print('Network connection error occurred');
} on LinkedInAuthException catch (e) {
print('Auth error: ${e.message}');
}
}
Future<String> exchangeCodeWithBackend(String code) async {
// Call your backend API and return the access_token string
return 'access_token_retrieved_from_backend';
}
Exception Types #
All exceptions inherit from LinkedInAuthException:
LinkedInAuthCancelledException: Thrown when the user closes the login window or sheet before completing the login flow.LinkedInAuthFailedException: Thrown when the flow fails due to a protocol, configuration, or server error.LinkedInAuthCodeMissingException: Thrown when the redirect URL loads successfully but does not contain thecodeparameter.LinkedInAuthNetworkException: Thrown when a connection issue occurs during the request.
Testing and Mocking #
The LinkedInSignIn client supports dependency injection for unit testing. You can supply a custom repository to mock network and authentication logic:
import 'package:linkedin_oauth2/linkedin_oauth2.dart';
class MockLinkedInAuthRepository implements LinkedInAuthRepository {
@override
Future<LinkedInAuthResult> getAuthorizationCode({
required String clientId,
required String redirectUri,
required List<String> scopes,
required String customScheme,
String? state,
}) async {
return const LinkedInAuthResult(code: 'mock_code', state: 'mock_state');
}
@override
Future<LinkedInUser> getUserProfile(String accessToken) async {
return const LinkedInUser(
sub: '123456',
name: 'John Doe',
email: 'john.doe@example.com',
);
}
}
void main() {
final mockClient = LinkedInSignIn(
clientId: 'test_client_id',
redirectUri: 'myapp://callback',
customScheme: 'myapp',
repository: MockLinkedInAuthRepository(),
);
}
License #
This project is licensed under the MIT License. See the LICENSE file for details.