react-native-linkedin-oauth2 1.0.1 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,710 @@
1
- ### react-native-linkedin-oauth2
1
+ # react-native-linkedin-oauth2
2
2
 
3
- A React Native module for LinkedIn OAuth2 authentication.
3
+ [![npm version](https://badge.fury.io/js/react-native-linkedin-oauth2.svg)](https://badge.fury.io/js/react-native-linkedin-oauth2)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ A simple, lightweight, and fully customizable React Native package for integrating LinkedIn OAuth2 authentication into your mobile apps. Built with TypeScript and designed for ease of use.
7
+
8
+ ## 📦 Installation
9
+
10
+ ```bash
11
+ npm install react-native-linkedin-oauth2
12
+ ```
13
+
14
+ or
15
+
16
+ ```bash
17
+ yarn add react-native-linkedin-oauth2
18
+ ```
19
+
20
+ ### Dependencies
21
+
22
+ This package requires the following peer dependencies:
23
+
24
+ ```bash
25
+ npm install react-native-webview react-native-safe-area-context
26
+ ```
27
+
28
+ or
29
+
30
+ ```bash
31
+ yarn add react-native-webview react-native-safe-area-context
32
+ ```
33
+
34
+ For iOS, run:
35
+
36
+ ```bash
37
+ cd ios && pod install
38
+ ```
39
+
40
+ ## 🔧 LinkedIn Developer Console Setup
41
+
42
+ Before using this package, you need to set up an OAuth2 application in the LinkedIn Developer Console:
43
+
44
+ 1. **Create a LinkedIn App**
45
+ - Go to [LinkedIn Developers](https://www.linkedin.com/developers/apps)
46
+ - Click "Create app"
47
+ - Fill in the required information
48
+
49
+ 2. **Configure OAuth Settings**
50
+ - Navigate to the "Auth" tab in your app settings
51
+ - Add your redirect URI (e.g., `https://yourapp.com/auth/linkedin/callback`)
52
+ - Note: The redirect URI must match exactly what you use in the component
53
+
54
+ 3. **Get Your Credentials**
55
+ - Copy your **Client ID** from the app settings
56
+ - Copy your **Client Secret** (keep this secure!)
57
+
58
+ 4. **Request Scopes**
59
+ - In the "Auth" tab, request the scopes you need
60
+ - Common scopes: `openid`, `profile`, `email`
61
+ - Additional scopes may require LinkedIn approval
62
+
63
+ ## 🚀 Quick Start
64
+
65
+ Here's a simple example to get you started:
66
+
67
+ ```tsx
68
+ import React, { useState } from 'react';
69
+ import { View, Button, Text } from 'react-native';
70
+ import { LinkedInModal, LinkedInProfile } from 'react-native-linkedin-oauth2';
71
+
72
+ const App = () => {
73
+ const [showModal, setShowModal] = useState(false);
74
+ const [user, setUser] = useState<LinkedInProfile | null>(null);
75
+
76
+ return (
77
+ <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
78
+ {user ? (
79
+ <View>
80
+ <Text>Welcome, {user.name}!</Text>
81
+ <Text>Email: {user.email}</Text>
82
+ </View>
83
+ ) : (
84
+ <Button
85
+ title="Sign in with LinkedIn"
86
+ onPress={() => setShowModal(true)}
87
+ />
88
+ )}
89
+
90
+ <LinkedInModal
91
+ isVisible={showModal}
92
+ clientId="YOUR_CLIENT_ID"
93
+ clientSecret="YOUR_CLIENT_SECRET"
94
+ redirectUri="https://yourapp.com/auth/linkedin/callback"
95
+ onSuccess={profile => {
96
+ console.log('Login successful:', profile);
97
+ setUser(profile);
98
+ setShowModal(false);
99
+ }}
100
+ onError={error => {
101
+ console.error('Login error:', error);
102
+ setShowModal(false);
103
+ }}
104
+ onClose={() => setShowModal(false)}
105
+ />
106
+ </View>
107
+ );
108
+ };
109
+
110
+ export default App;
111
+ ```
112
+
113
+ ## 📚 API Reference
114
+
115
+ ### LinkedInModal Props
116
+
117
+ | Prop | Type | Required | Default | Description |
118
+ | ---------------- | ----------------------------------------------- | -------- | ---------------------------- | --------------------------------------------------------- |
119
+ | `isVisible` | `boolean` | ✅ | - | Controls the visibility of the modal |
120
+ | `clientId` | `string` | ✅ | - | Your LinkedIn Client ID |
121
+ | `clientSecret` | `string` | ✅ | - | Your LinkedIn Client Secret |
122
+ | `redirectUri` | `string` | ✅ | - | The redirect URI registered in LinkedIn Developer Console |
123
+ | `scope` | `string` | ❌ | `'openid email profile'` | Space-separated list of OAuth scopes |
124
+ | `onSuccess` | `(user: LinkedInProfile) => void` | ❌ | `() => {}` | Callback invoked when authentication succeeds |
125
+ | `onError` | `(error: Error) => void` | ❌ | `() => {}` | Callback invoked when an error occurs |
126
+ | `onClose` | `() => void` | ❌ | `() => {}` | Callback invoked when the modal is closed |
127
+ | `onLogout` | `() => void` | ❌ | `() => {}` | Callback invoked when logout completes |
128
+ | `logout` | `boolean` | ❌ | `false` | If true, shows LinkedIn logout page instead of login |
129
+ | `closeOnSuccess` | `boolean` | ❌ | `true` | Automatically close modal after successful login |
130
+ | `renderHeader` | `(props: { onClose: () => void }) => ReactNode` | ❌ | - | Custom header component |
131
+ | `renderLoading` | `() => ReactNode` | ❌ | - | Custom loading indicator |
132
+ | `containerStyle` | `StyleProp<ViewStyle>` | ❌ | - | Style for the container SafeAreaView |
133
+ | `wrapperStyle` | `StyleProp<ViewStyle>` | ❌ | - | Style for the wrapper View |
134
+ | `modalProps` | `Partial<Omit<ModalProps, 'visible'>>` | ❌ | `{ animationType: 'slide' }` | Additional props for React Native Modal |
135
+
136
+ ### LinkedInProfile Type
137
+
138
+ The `LinkedInProfile` interface represents the user data returned after successful authentication:
139
+
140
+ ```typescript
141
+ interface LinkedInProfile {
142
+ sub: string; // Unique user identifier
143
+ name: string; // Full name
144
+ given_name: string; // First name
145
+ family_name: string; // Last name
146
+ picture: string; // Profile picture URL
147
+ email?: string; // Email (if 'email' scope requested)
148
+ email_verified?: boolean; // Email verification status
149
+ locale: {
150
+ country: string; // Country code (e.g., 'US')
151
+ language: string; // Language code (e.g., 'en')
152
+ };
153
+ [key: string]: any; // Additional fields
154
+ }
155
+ ```
156
+
157
+ ### LinkedInTokenResponse Type
158
+
159
+ The `LinkedInTokenResponse` interface represents the OAuth token response:
160
+
161
+ ```typescript
162
+ interface LinkedInTokenResponse {
163
+ access_token: string; // Access token for API requests
164
+ expires_in: number; // Token expiration time in seconds
165
+ scope: string; // Granted scopes
166
+ token_type: string; // Token type (usually 'Bearer')
167
+ id_token?: string; // ID token (if OpenID scope requested)
168
+ }
169
+ ```
170
+
171
+ ## 🎨 Advanced Usage
172
+
173
+ ### Custom Scopes
174
+
175
+ Request additional permissions by specifying custom scopes:
176
+
177
+ ```tsx
178
+ <LinkedInModal
179
+ isVisible={showModal}
180
+ clientId="YOUR_CLIENT_ID"
181
+ clientSecret="YOUR_CLIENT_SECRET"
182
+ redirectUri="YOUR_REDIRECT_URI"
183
+ scope="openid profile email w_member_social" // Request posting permissions
184
+ onSuccess={profile => console.log(profile)}
185
+ onError={error => console.error(error)}
186
+ onClose={() => setShowModal(false)}
187
+ />
188
+ ```
189
+
190
+ ### Custom Header
191
+
192
+ Provide your own header component:
193
+
194
+ ```tsx
195
+ const CustomHeader = ({ onClose }: { onClose: () => void }) => (
196
+ <View
197
+ style={{
198
+ height: 60,
199
+ backgroundColor: '#0077B5',
200
+ justifyContent: 'center',
201
+ alignItems: 'center',
202
+ }}
203
+ >
204
+ <TouchableOpacity
205
+ onPress={onClose}
206
+ style={{ position: 'absolute', right: 20 }}
207
+ >
208
+ <Text style={{ color: 'white', fontSize: 16 }}>✕</Text>
209
+ </TouchableOpacity>
210
+ <Text style={{ color: 'white', fontSize: 18, fontWeight: 'bold' }}>
211
+ Sign in with LinkedIn
212
+ </Text>
213
+ </View>
214
+ );
215
+
216
+ <LinkedInModal
217
+ isVisible={showModal}
218
+ clientId="YOUR_CLIENT_ID"
219
+ clientSecret="YOUR_CLIENT_SECRET"
220
+ redirectUri="YOUR_REDIRECT_URI"
221
+ renderHeader={props => <CustomHeader {...props} />}
222
+ onSuccess={profile => console.log(profile)}
223
+ onError={error => console.error(error)}
224
+ onClose={() => setShowModal(false)}
225
+ />;
226
+ ```
227
+
228
+ ### Custom Loading Indicator
229
+
230
+ Customize the loading experience:
231
+
232
+ ```tsx
233
+ const CustomLoading = () => (
234
+ <View
235
+ style={{
236
+ position: 'absolute',
237
+ top: 0,
238
+ left: 0,
239
+ right: 0,
240
+ bottom: 0,
241
+ justifyContent: 'center',
242
+ alignItems: 'center',
243
+ backgroundColor: 'rgba(0, 119, 181, 0.1)',
244
+ }}
245
+ >
246
+ <ActivityIndicator size="large" color="#0077B5" />
247
+ <Text style={{ marginTop: 10, color: '#0077B5' }}>
248
+ Connecting to LinkedIn...
249
+ </Text>
250
+ </View>
251
+ );
252
+
253
+ <LinkedInModal
254
+ isVisible={showModal}
255
+ clientId="YOUR_CLIENT_ID"
256
+ clientSecret="YOUR_CLIENT_SECRET"
257
+ redirectUri="YOUR_REDIRECT_URI"
258
+ renderLoading={() => <CustomLoading />}
259
+ onSuccess={profile => console.log(profile)}
260
+ onError={error => console.error(error)}
261
+ onClose={() => setShowModal(false)}
262
+ />;
263
+ ```
264
+
265
+ ### Logout Functionality
266
+
267
+ Log out users from LinkedIn:
268
+
269
+ ```tsx
270
+ const [showLogoutModal, setShowLogoutModal] = useState(false);
271
+
272
+ <LinkedInModal
273
+ isVisible={showLogoutModal}
274
+ logout={true}
275
+ onLogout={() => {
276
+ console.log('User logged out');
277
+ setShowLogoutModal(false);
278
+ }}
279
+ onClose={() => setShowLogoutModal(false)}
280
+ />;
281
+ ```
282
+
283
+ ### Custom Styling
284
+
285
+ Customize the modal appearance:
286
+
287
+ ```tsx
288
+ <LinkedInModal
289
+ isVisible={showModal}
290
+ clientId="YOUR_CLIENT_ID"
291
+ clientSecret="YOUR_CLIENT_SECRET"
292
+ redirectUri="YOUR_REDIRECT_URI"
293
+ containerStyle={{ backgroundColor: '#f5f5f5' }}
294
+ wrapperStyle={{ borderRadius: 10, overflow: 'hidden' }}
295
+ modalProps={{
296
+ animationType: 'fade',
297
+ transparent: true,
298
+ }}
299
+ onSuccess={profile => console.log(profile)}
300
+ onError={error => console.error(error)}
301
+ onClose={() => setShowModal(false)}
302
+ />
303
+ ```
304
+
305
+ ### Keep Modal Open After Success
306
+
307
+ Sometimes you may want to manually control when to close the modal:
308
+
309
+ ```tsx
310
+ <LinkedInModal
311
+ isVisible={showModal}
312
+ clientId="YOUR_CLIENT_ID"
313
+ clientSecret="YOUR_CLIENT_SECRET"
314
+ redirectUri="YOUR_REDIRECT_URI"
315
+ closeOnSuccess={false} // Don't auto-close
316
+ onSuccess={profile => {
317
+ console.log('Login successful:', profile);
318
+ // Do something with the profile...
319
+ // Then manually close when ready
320
+ setTimeout(() => setShowModal(false), 2000);
321
+ }}
322
+ onError={error => console.error(error)}
323
+ onClose={() => setShowModal(false)}
324
+ />
325
+ ```
326
+
327
+ ## 🔍 Complete Example
328
+
329
+ Here's a full-featured example with all the props and callbacks included:
330
+
331
+ ```tsx
332
+ import React, { useState } from 'react';
333
+ import {
334
+ View,
335
+ Button,
336
+ Text,
337
+ Image,
338
+ StyleSheet,
339
+ TouchableOpacity,
340
+ ActivityIndicator,
341
+ } from 'react-native';
342
+ import { LinkedInModal, LinkedInProfile } from 'react-native-linkedin-oauth2';
343
+
344
+ const LINKEDIN_CONFIG = {
345
+ clientId: 'YOUR_CLIENT_ID',
346
+ clientSecret: 'YOUR_CLIENT_SECRET',
347
+ redirectUri: 'https://yourapp.com/auth/linkedin/callback',
348
+ };
349
+
350
+ const App = () => {
351
+ const [showModal, setShowModal] = useState(false);
352
+ const [showLogoutModal, setShowLogoutModal] = useState(false);
353
+ const [user, setUser] = useState<LinkedInProfile | null>(null);
354
+ const [error, setError] = useState<string | null>(null);
355
+
356
+ const handleLogin = () => {
357
+ setError(null);
358
+ setShowModal(true);
359
+ };
360
+
361
+ const handleSuccess = (profile: LinkedInProfile) => {
362
+ console.log('Authentication successful:', profile);
363
+ setUser(profile);
364
+ setError(null);
365
+ };
366
+
367
+ const handleError = (err: Error) => {
368
+ console.error('Authentication error:', err);
369
+ setError(err.message);
370
+ };
371
+
372
+ const handleLogout = () => {
373
+ setShowLogoutModal(true);
374
+ };
375
+
376
+ const CustomHeader = ({ onClose }: { onClose: () => void }) => (
377
+ <View style={styles.header}>
378
+ <Text style={styles.headerTitle}>Sign in with LinkedIn</Text>
379
+ <TouchableOpacity onPress={onClose} style={styles.closeButton}>
380
+ <Text style={styles.closeButtonText}>✕</Text>
381
+ </TouchableOpacity>
382
+ </View>
383
+ );
384
+
385
+ const CustomLoading = () => (
386
+ <View style={styles.loadingContainer}>
387
+ <ActivityIndicator size="large" color="#0077B5" />
388
+ <Text style={styles.loadingText}>Authenticating...</Text>
389
+ </View>
390
+ );
391
+
392
+ return (
393
+ <View style={styles.container}>
394
+ {user ? (
395
+ <View style={styles.profileContainer}>
396
+ <Image source={{ uri: user.picture }} style={styles.profileImage} />
397
+ <Text style={styles.userName}>{user.name}</Text>
398
+ <Text style={styles.userEmail}>{user.email}</Text>
399
+ <Text style={styles.userId}>ID: {user.sub}</Text>
400
+ <Button title="Logout" onPress={handleLogout} color="#dc3545" />
401
+ </View>
402
+ ) : (
403
+ <View style={styles.loginContainer}>
404
+ <Text style={styles.title}>LinkedIn OAuth2 Example</Text>
405
+ {error && <Text style={styles.errorText}>{error}</Text>}
406
+ <Button
407
+ title="Sign in with LinkedIn"
408
+ onPress={handleLogin}
409
+ color="#0077B5"
410
+ />
411
+ </View>
412
+ )}
413
+
414
+ {/* Login Modal */}
415
+ <LinkedInModal
416
+ isVisible={showModal}
417
+ clientId={LINKEDIN_CONFIG.clientId}
418
+ clientSecret={LINKEDIN_CONFIG.clientSecret}
419
+ redirectUri={LINKEDIN_CONFIG.redirectUri}
420
+ scope="openid profile email"
421
+ onSuccess={handleSuccess}
422
+ onError={handleError}
423
+ onClose={() => setShowModal(false)}
424
+ renderHeader={props => <CustomHeader {...props} />}
425
+ renderLoading={() => <CustomLoading />}
426
+ closeOnSuccess={true}
427
+ />
428
+
429
+ {/* Logout Modal */}
430
+ <LinkedInModal
431
+ isVisible={showLogoutModal}
432
+ logout={true}
433
+ onLogout={() => {
434
+ console.log('Logout successful');
435
+ setUser(null);
436
+ setShowLogoutModal(false);
437
+ }}
438
+ onClose={() => setShowLogoutModal(false)}
439
+ />
440
+ </View>
441
+ );
442
+ };
443
+
444
+ const styles = StyleSheet.create({
445
+ container: {
446
+ flex: 1,
447
+ justifyContent: 'center',
448
+ alignItems: 'center',
449
+ backgroundColor: '#f5f5f5',
450
+ },
451
+ loginContainer: {
452
+ alignItems: 'center',
453
+ padding: 20,
454
+ },
455
+ title: {
456
+ fontSize: 24,
457
+ fontWeight: 'bold',
458
+ marginBottom: 20,
459
+ color: '#333',
460
+ },
461
+ profileContainer: {
462
+ alignItems: 'center',
463
+ padding: 20,
464
+ backgroundColor: 'white',
465
+ borderRadius: 10,
466
+ shadowColor: '#000',
467
+ shadowOffset: { width: 0, height: 2 },
468
+ shadowOpacity: 0.1,
469
+ shadowRadius: 4,
470
+ elevation: 3,
471
+ },
472
+ profileImage: {
473
+ width: 100,
474
+ height: 100,
475
+ borderRadius: 50,
476
+ marginBottom: 15,
477
+ },
478
+ userName: {
479
+ fontSize: 22,
480
+ fontWeight: 'bold',
481
+ color: '#333',
482
+ marginBottom: 5,
483
+ },
484
+ userEmail: {
485
+ fontSize: 16,
486
+ color: '#666',
487
+ marginBottom: 5,
488
+ },
489
+ userId: {
490
+ fontSize: 12,
491
+ color: '#999',
492
+ marginBottom: 20,
493
+ },
494
+ errorText: {
495
+ color: 'red',
496
+ marginBottom: 10,
497
+ textAlign: 'center',
498
+ },
499
+ header: {
500
+ height: 60,
501
+ backgroundColor: '#0077B5',
502
+ justifyContent: 'center',
503
+ alignItems: 'center',
504
+ flexDirection: 'row',
505
+ },
506
+ headerTitle: {
507
+ color: 'white',
508
+ fontSize: 18,
509
+ fontWeight: 'bold',
510
+ },
511
+ closeButton: {
512
+ position: 'absolute',
513
+ right: 20,
514
+ padding: 10,
515
+ },
516
+ closeButtonText: {
517
+ color: 'white',
518
+ fontSize: 24,
519
+ fontWeight: 'bold',
520
+ },
521
+ loadingContainer: {
522
+ position: 'absolute',
523
+ top: 0,
524
+ left: 0,
525
+ right: 0,
526
+ bottom: 0,
527
+ justifyContent: 'center',
528
+ alignItems: 'center',
529
+ backgroundColor: 'rgba(0, 119, 181, 0.1)',
530
+ },
531
+ loadingText: {
532
+ marginTop: 10,
533
+ color: '#0077B5',
534
+ fontSize: 16,
535
+ },
536
+ });
537
+
538
+ export default App;
539
+ ```
540
+
541
+ ## 🛠️ Troubleshooting
542
+
543
+ ### Common Issues
544
+
545
+ #### 1. "Missing required props" Warning
546
+
547
+ **Problem:** You're seeing a warning about missing `clientId`, `clientSecret`, or `redirectUri`.
548
+
549
+ **Solution:** Ensure all three required props are provided:
550
+
551
+ ```tsx
552
+ <LinkedInModal
553
+ isVisible={true}
554
+ clientId="YOUR_CLIENT_ID" // ✅ Required
555
+ clientSecret="YOUR_CLIENT_SECRET" // ✅ Required
556
+ redirectUri="YOUR_REDIRECT_URI" // ✅ Required
557
+ // ...other props
558
+ />
559
+ ```
560
+
561
+ #### 2. "Failed to get access token" Error
562
+
563
+ **Problem:** The token exchange fails.
564
+
565
+ **Possible causes:**
566
+
567
+ - Incorrect `clientSecret`
568
+ - Redirect URI mismatch between code and LinkedIn Developer Console
569
+ - Expired or invalid authorization code
570
+
571
+ **Solution:**
572
+
573
+ - Verify your credentials in the LinkedIn Developer Console
574
+ - Ensure the `redirectUri` prop matches exactly what's registered in your app settings
575
+ - Check that your app is not in a restricted mode in the developer console
576
+
577
+ #### 3. Network Request Failed
578
+
579
+ **Problem:** Unable to connect to LinkedIn servers.
580
+
581
+ **Solution:**
582
+
583
+ - Check your internet connection
584
+ - Ensure your app has network permissions (especially on Android)
585
+ - Verify that LinkedIn's OAuth endpoints are accessible from your network
586
+
587
+ #### 4. Email is Undefined
588
+
589
+ **Problem:** The `user.email` field is `undefined` after successful login.
590
+
591
+ **Solution:**
592
+
593
+ - Make sure you've requested the `email` scope:
594
+ ```tsx
595
+ <LinkedInModal scope="openid profile email" />
596
+ ```
597
+ - Verify that the `email` scope is approved in your LinkedIn app settings
598
+
599
+ #### 5. WebView Not Loading
600
+
601
+ **Problem:** The WebView appears blank or doesn't load.
602
+
603
+ **Solution:**
604
+
605
+ - Ensure `react-native-webview` is properly installed
606
+ - For iOS, run `cd ios && pod install`
607
+ - Check that you've linked the library correctly (this should be automatic with auto-linking)
608
+
609
+ #### 6. Modal Styling Issues
610
+
611
+ **Problem:** The modal doesn't look right or doesn't fit the screen.
612
+
613
+ **Solution:**
614
+
615
+ - Ensure `react-native-safe-area-context` is installed and configured
616
+ - Wrap your root app component with `SafeAreaProvider`:
617
+
618
+ ```tsx
619
+ import { SafeAreaProvider } from 'react-native-safe-area-context';
620
+
621
+ const App = () => (
622
+ <SafeAreaProvider>{/* Your app content */}</SafeAreaProvider>
623
+ );
624
+ ```
625
+
626
+ ## 🔒 Security Considerations
627
+
628
+ > [!CAUTION]
629
+ > **Client Secret Exposure:** The `clientSecret` is included in your React Native app bundle, which means it can be extracted by determined users. For maximum security, consider implementing a backend proxy that handles the token exchange instead of doing it directly in the mobile app.
630
+
631
+ ### Recommended Secure Implementation
632
+
633
+ Instead of passing the `clientSecret` directly:
634
+
635
+ 1. Create a backend endpoint that accepts the authorization code
636
+ 2. Have your backend exchange the code for a token using the `clientSecret`
637
+ 3. Return the profile data (or your own JWT) to the mobile app
638
+
639
+ This way, the `clientSecret` stays secure on your server.
640
+
641
+ ## 📄 TypeScript Support
642
+
643
+ This package is written in TypeScript and includes full type definitions. No need for `@types/` packages!
644
+
645
+ **Exported Types:**
646
+
647
+ - `LinkedInProfile` - User profile data structure
648
+ - `LinkedInTokenResponse` - OAuth token response structure
649
+ - `LinkedInModalProps` - Component props interface
650
+
651
+ ```typescript
652
+ import {
653
+ LinkedInModal,
654
+ LinkedInProfile,
655
+ LinkedInTokenResponse,
656
+ LinkedInModalProps,
657
+ } from 'react-native-linkedin-oauth2';
658
+
659
+ const handleSuccess = (profile: LinkedInProfile) => {
660
+ // TypeScript knows the structure of profile
661
+ console.log(profile.name, profile.email);
662
+ };
663
+ ```
664
+
665
+ ## 📱 Supported Platforms
666
+
667
+ - ✅ iOS
668
+ - ✅ Android
669
+ - ❌ Web (requires different OAuth flow)
670
+
671
+ ## 🤝 Contributing
672
+
673
+ Contributions are welcome! Please feel free to submit a Pull Request.
674
+
675
+ 1. Fork the repository
676
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
677
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
678
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
679
+ 5. Open a Pull Request
680
+
681
+ ## 📝 License
682
+
683
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
684
+
685
+ ## 👨‍💻 Author
686
+
687
+ **Nikhil Wankhede**
688
+
689
+ - GitHub: [@NikhilRW](https://github.com/NikhilRW)
690
+
691
+ ## 🙏 Acknowledgments
692
+
693
+ - Built with [react-native-webview](https://github.com/react-native-webview/react-native-webview)
694
+ - Uses [react-native-safe-area-context](https://github.com/th3rdwave/react-native-safe-area-context)
695
+
696
+ ## 📦 Related Packages
697
+
698
+ - [react-native-webview](https://www.npmjs.com/package/react-native-webview) - WebView component for React Native
699
+ - [react-native-safe-area-context](https://www.npmjs.com/package/react-native-safe-area-context) - Safe area context for React Native
700
+
701
+ ## 🔗 Links
702
+
703
+ - [NPM Package](https://www.npmjs.com/package/react-native-linkedin-oauth2)
704
+ - [GitHub Repository](https://github.com/NikhilRW/react-native-linkedin-oauth2)
705
+ - [LinkedIn Developers Portal](https://www.linkedin.com/developers/)
706
+ - [LinkedIn OAuth 2.0 Documentation](https://docs.microsoft.com/en-us/linkedin/shared/authentication/authentication)
707
+
708
+ ---
709
+
710
+ **Made with ❤️ by Nikhil Wankhede**