A modification to XCreds that makes ClassLink work properly as an OIDC identity provider for macOS login. Requires XCreds 5.9+ (getToken() added a required basicAuth parameter in 5.9).
ClassLink's OIDC implementation always redirects to a generic "Find your login page" screen at launchpad.classlink.com, no matter how you configure things. Users have to search for and select their district before they can log in.
I opened a case with ClassLink about this (Case #00606606). Their response:
"The redirect to the generic ClassLink page is intended with the OIDC/OAuth2 workflow. The reason being is this will allow for universal functionality. Unfortunately we cannot manipulate the issuer url to redirect to [your] login page. If JAMF Connect supports a SAML integration, this can be specified in the ClassLink SAML settings."
Neither Jamf Connect nor XCreds use SAML for login window auth. They use OIDC. I submitted feature requests to both ClassLink and Jamf, but as of early 2026 neither has addressed it.
One file is modified: WebViewController.swift. It adds two things.
Stock XCreds handles OIDC redirects via didReceiveServerRedirectForProvisionalNavigation, which only catches HTTP 3xx server-side redirects. ClassLink uses client-side redirects, so stock XCreds never sees them and the webview ends up trying to load whatever your redirect URI points to (usually your school's website or localhost).
This patch adds an intercept in decidePolicyFor, which fires for all navigations including client-side redirects. When it sees a URL matching your configured redirectURI, it cancels the navigation before any HTTP request goes out, extracts the authorization code from the query string, and exchanges it for tokens. The user sees a "Signing in..." message during the exchange.
When ClassLink loads the generic search page, JavaScript is injected that:
- Shows a white overlay with a spinner ("Loading YourDistrict login...")
- Searches for your tenant code in ClassLink's search bar
- Clicks the matching result
The user never sees the generic search page. They see a brief spinner, then their district's login screen. If the script fails (ClassLink redesigns their page, network issue, etc.), a 5-second timeout removes the overlay and falls back to the generic search page.
ClassLink has two URL formats for district login pages. Check which one yours uses before configuring.
| URL style | Example | Status |
|---|---|---|
| Launchpad (most districts) | launchpad.classlink.com/yourdistrict |
Working |
| Login (districts with passkeys) | login.classlink.com/my/yourdistrict |
Working, lightly tested |
To check: visit launchpad.classlink.com/yourdistrict in a browser. If it stays on launchpad.classlink.com, you have the Launchpad style. If it redirects to login.classlink.com/my/yourdistrict, you have the Login style.
As of April 2026, login.classlink.com support is working but has limited testing outside of one external district.
https://login.classlink.com/.well-known/openid-configurationdoes not serve an OIDC discovery document (returns HTML). All of ClassLink's OIDC infrastructure still lives atlaunchpad.classlink.com.- Set
idpHostNametolaunchpad.classlink.com, notlogin.classlink.com. shouldSetGoogleAccessTypeToOfflinemust betrue. ClassLink's token response often omits the refresh token, and this setting tells XCreds to accept that. Without it, token validation will fail.- Password capture is handled differently on
login.classlink.comdue to the page using a different frontend framework thanlaunchpad.classlink.com.
| Key | Type | Required | Description |
|---|---|---|---|
classLinkTenant |
String | Yes | Your tenant code. The slug after launchpad.classlink.com/ or login.classlink.com/my/. |
classLinkTenantDisplayName |
String | No | Friendly name shown on the loading overlay. Defaults to the search term or tenant code. |
classLinkSearchTerm |
String | No | Use this if your tenant code doesn't work as a ClassLink search term. Some districts can only be found by district name, not tenant code. |
To check if you need classLinkSearchTerm: go to launchpad.classlink.com and type your tenant code into the search bar. If your district shows up, you don't need it. If it doesn't, try your district name instead.
| Key | Value |
|---|---|
discoveryURL |
https://launchpad.classlink.com/.well-known/openid-configuration |
clientID |
Your ClassLink OIDC Client ID (from the Developer portal) |
clientSecret |
Your ClassLink OIDC Client Secret |
redirectURI |
The redirect URI configured in your ClassLink app (see below) |
idpHostName |
launchpad.classlink.com (for both Launchpad and Login style districts) |
shouldSetGoogleAccessTypeToOffline |
true (tells XCreds to accept token responses without a refresh token) |
ClassLink requires your redirect URI's domain to be verified in their Developer portal. We weren't able to get localhost or 127.0.0.1 verified, so we used our school's homepage URL.
ClassLink appends the authorization code to the redirect URI (e.g. https://www.yourschool.org/?code=abc123), and the interceptor catches it before any request is made, cancels the navigation, and completes the OAuth exchange.
Make sure the redirectURI in your XCreds config matches exactly what's set in your ClassLink Developer app.
- Create an OIDC application in the ClassLink Developer portal
- Add your redirect URI domain as a verified domain
- Set the redirect URI to match your XCreds config profile
- Note your Client ID and Client Secret
See example-classlink.mobileconfig for a working example.
XCreds captures the password from the ClassLink login form at each login and sets the local account password to match. There's no real-time sync while logged in, the local password updates the next time the user logs in through the login window.
XCreds also has a PasswordOverwriteSilent preference key that silently resets the keychain if the IdP password changed since the last login.
- XCreds by Twocanoes Software (Timothy Perfitt)
- ClassLink integration by Brad White, Peninsula School District
