iOS Swift中LinkedIn OAuth 2.0认证实现及重定向URL配置问题
Hey there, let's work through this LinkedIn OAuth 2.0 migration issue step by step—your redirect URI mismatch is the core problem here, and we'll fix that first, then walk through a full working implementation.
First: Fix the "redirect_uri does not match" Error
This error happens because your app's configured redirect URL doesn't exactly match what's registered in LinkedIn's developer portal. Since you want to redirect back to your app, you'll need to set up a custom URL scheme:
1. Configure Custom URL Scheme in Xcode
- Open your project's
Info.plist - Right-click → Add Row → Select
URL types - Expand the item:
- Set
URL Schemesto a unique value (e.g.,myapplinkedin—avoid generic names likemyappto reduce conflicts) - You can leave
RoleasEditorandURL Identifieras your app's bundle ID for clarity
- Set
2. Register the Redirect URI in LinkedIn Developer Portal
- Log into the LinkedIn Developer Portal and open your app
- Navigate to Auth → OAuth 2.0 settings
- In the
Authorized redirect URLs for your appsection, add your custom URL (e.g.,myapplinkedin://auth—the pathauthis optional but helps distinguish auth redirects from other app links) - Save your changes (this can take a few minutes to propagate, so don't panic if it doesn't work immediately)
⚠️ Critical: The redirect URI in your code must exactly match what you registered here—no typos, extra slashes, or case differences.
Full OAuth 2.0 Implementation for iOS (No Deprecated SDKs)
We'll use the authorization code flow (LinkedIn's recommended secure flow for mobile apps) instead of the old SDK. Here's the step-by-step code:
Step 1: Build the Authorization Request URL
We'll construct the URL that opens LinkedIn's login page via SFSafariViewController (LinkedIn discourages using WebViews for security):
import SafariServices func buildLinkedInAuthURL() -> URL? { let baseURL = "https://www.linkedin.com/oauth/v2/authorization" let components = URLComponents(string: baseURL)? .addingQueryItem(name: "response_type", value: "code") // Authorization code flow .addingQueryItem(name: "client_id", value: "YOUR_LINKEDIN_CLIENT_ID") .addingQueryItem(name: "redirect_uri", value: "myapplinkedin://auth") // Match your registered URI .addingQueryItem(name: "scope", value: "r_liteprofile r_emailaddress") // Request required scopes .addingQueryItem(name: "state", value: UUID().uuidString) // CSRF protection return components?.url }
Step 2: Launch the Login Flow
func startLinkedInAuth() { guard let authURL = buildLinkedInAuthURL() else { print("Failed to build auth URL") return } let safariVC = SFSafariViewController(url: authURL) safariVC.delegate = self present(safariVC, animated: true) }
Step 3: Handle the Redirect Back to Your App
Add this code to your SceneDelegate (iOS 13+) or AppDelegate:
For SceneDelegate:
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) { guard let url = URLContexts.first?.url else { return } handleLinkedInRedirect(url: url) } private func handleLinkedInRedirect(url: URL) { guard url.scheme == "myapplinkedin", url.host == "auth" else { return } let components = URLComponents(url: url, resolvingAgainstBaseURL: false) guard let code = components?.queryItems?.first(where: { $0.name == "code" })?.value else { print("No authorization code found in redirect") return } // Exchange code for access token exchangeCodeForAccessToken(code: code) }
For AppDelegate:
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { handleLinkedInRedirect(url: url) return true } private func handleLinkedInRedirect(url: URL) { // Same code as above for parsing the authorization code }
Step 4: Exchange Authorization Code for Access Token
Send a POST request to LinkedIn's token endpoint to get your access token:
func exchangeCodeForAccessToken(code: String) { let tokenURL = "https://www.linkedin.com/oauth/v2/accessToken" let parameters = [ "grant_type": "authorization_code", "code": code, "client_id": "YOUR_LINKEDIN_CLIENT_ID", "client_secret": "YOUR_LINKEDIN_CLIENT_SECRET", "redirect_uri": "myapplinkedin://auth" // Match your registered URI ] var request = URLRequest(url: URL(string: tokenURL)!) request.httpMethod = "POST" request.httpBody = parameters.percentEncoded() request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") URLSession.shared.dataTask(with: request) { data, response, error in guard let data = data, error == nil else { print("Token exchange error: \(error?.localizedDescription ?? "Unknown error")") return } do { if let tokenResponse = try JSONSerialization.jsonObject(with: data) as? [String: Any], let accessToken = tokenResponse["access_token"] as? String { // Success! Use the token to fetch user data DispatchQueue.main.async { let alertVC = UIAlertController(title: "Success", message: "Access Token: \(accessToken)", preferredStyle: .alert) alertVC.addAction(UIAlertAction(title: "OK", style: .default)) self.present(alertVC, animated: true) } } } catch { print("Failed to parse token response: \(error.localizedDescription)") } }.resume() } // Helper extension to encode POST parameters extension Dictionary where Key == String, Value == String { func percentEncoded() -> Data? { return map { key, value in let escapedKey = key.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)! let escapedValue = value.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)! return "\(escapedKey)=\(escapedValue)" } .joined(separator: "&") .data(using: .utf8) } } extension CharacterSet { static let urlQueryValueAllowed: CharacterSet = { let generalDelimitersToEncode = ":#[]@" let subDelimitersToEncode = "!$&'()*+,;=" var allowed = CharacterSet.urlQueryAllowed allowed.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)") return allowed }() }
Key Notes to Avoid Issues
- Scopes: Make sure the scopes you request (e.g.,
r_liteprofile) are approved in your LinkedIn app's Auth → Scopes section. Unapproved scopes will cause errors. - Security: Store your access token in Keychain instead of
UserDefaultsfor better security. - Third-Party Libraries: If you prefer using a library, look for ones actively maintained for LinkedIn's current OAuth 2.0 flow (avoid outdated libraries that rely on the deprecated SDK).
内容的提问来源于stack exchange,提问作者swapnil patel

