A Swift API for signing in to Apple's developer services.
XcodesLoginKit handles Apple's SRP password login, federated Apple IDs, two-factor authentication, session validation, logout, and optional fastlane/Spaceship cookie import.
Add the package to your Package.swift:
.package(url: "https://github.com/XcodesOrg/XcodesLoginKit", branch: "main")Then add the product you need:
.product(name: "XcodesLoginKit", package: "XcodesLoginKit")Use Client when your app wants to drive its own sign-in UI:
import Foundation
import XcodesLoginKit
let client = Client()
let state = try await client.authenticationState(
accountName: "name@example.com",
password: password
)
switch state {
case .authenticated(let session):
print("Signed in as \(session.user.fullName ?? "Apple Developer")")
case .waitingForFederatedAuthentication(let federation):
guard let idpURL = federation.idpURL else {
throw AuthenticationError.federatedAuthenticationRequired
}
// Open idpURL in a browser. After the user signs in, collect the redirected URL.
let callbackURLString = "... pasted or received callback URL ..."
try await client.validateFederatedCallbackURLString(callbackURLString)
case .waitingForSecondFactor(let option, let authOptions, let sessionData):
switch option {
case .codeSent:
let code = "... code from trusted device ..."
try await client.submitSecurityCode(.device(code: code), sessionData: sessionData)
case .smsPendingChoice:
guard let phoneNumber = authOptions.trustedPhoneNumbers?.first else { return }
try await client.requestSMSSecurityCode(
to: phoneNumber,
authOptions: authOptions,
sessionData: sessionData
)
case .smsSent(let phoneNumber):
let code = "... code from SMS ..."
try await client.submitSecurityCode(
.sms(code: code, phoneNumberId: phoneNumber.id),
sessionData: sessionData
)
case .securityKey:
// Add the XcodesLoginKitSecurityKey product and call
// submitSecurityKeyPinCode(_:sessionData:authOptions:).
break
}
case .unauthenticated, .notAppleDeveloper:
break
}- Create a
Client. Pass a customURLSessionif you want isolated cookie storage. - Call
authenticationState(accountName:password:). - Switch on
AuthenticationState. - Complete any required federated authentication or second-factor challenge.
- Call
validateSession()later to check whether stored cookies are still valid. - Call
signout()to clear the client's cookies.
Federated accounts return .waitingForFederatedAuthentication. Open FederationResponse.idpURL
in a browser, let the user finish identity-provider sign-in, then pass the final callback URL to
validateFederatedCallbackURL(_:) or validateFederatedCallbackURLString(_:).
If the user already has a fastlane session, load its cookies into a session and pass that session
to Client:
let loader = FastlaneSessionLoader()
let session = try loader.session(
fastlaneUser: "name@example.com",
environmentValue: { ProcessInfo.processInfo.environment[$0] }
)
let client = Client(urlSession: session)
let state = try await client.validateSession()Use FastlaneSessionLoader.Constants.fastlaneSessionEnvVarName as fastlaneUser to load cookies
from FASTLANE_SESSION instead of ~/.fastlane/spaceship/<apple-id>/cookie.
AppleSessionService wraps the low-level client with dependency-injected environment lookup,
keychain storage, prompts, browser opening, validation, login, and logout. It is a good fit for
command-line tools that want a "login if needed" workflow while still controlling where credentials
are stored and how users are prompted.
Add the XcodesLoginKitSecurityKey product when you need FIDO2 security-key support:
.product(name: "XcodesLoginKitSecurityKey", package: "XcodesLoginKit")When the login state is .waitingForSecondFactor(.securityKey, authOptions, sessionData), call
submitSecurityKeyPinCode(_:sessionData:authOptions:).