PVCheevos/LOGIN-GUIDE.md
This guide covers all authentication methods supported by the PVCheevos library, including the new username/password login functionality.
The web API key method is the most secure and recommended approach:
Advantages:
How to get your API key:
import PVCheevos
// Create client with API key
let client = PVCheevos.client(
username: "your_username",
webAPIKey: "your_web_api_key"
)
// Ready to use immediately
let profile = try await client.getUserProfile(username: "your_username")
The username/password method provides a familiar login experience:
Advantages:
Use cases:
import PVCheevos
// Basic setup
let client = PVCheevos.client(
username: "MaxMilyin",
webAPIKey: "ABC123XYZ789"
)
// Validate credentials
let isValid = await client.validateCredentials()
print("Credentials valid: \(isValid)")
// Use the API
let profile = try await client.getUserProfile(username: "MaxMilyin")
print("User: \(profile.user), Points: \(profile.totalPoints ?? 0)")
import PVCheevos
// Create client for password authentication
let client = PVCheevos.clientWithPassword(
username: "MaxMilyin",
password: "mySecurePassword"
)
// Perform login
do {
let session = try await client.login(
username: "MaxMilyin",
password: "mySecurePassword"
)
print("ā
Login successful!")
print("Username: \(session.username)")
print("Token: \(session.token)")
print("Score: \(session.score ?? 0)")
print("Softcore Score: \(session.softcoreScore ?? 0)")
print("Permissions: \(session.permissions ?? 0)")
print("Account Type: \(session.accountType ?? "Unknown")")
// Now you can use the API
let profile = try await client.getUserProfile(username: session.username)
print("Profile loaded: \(profile.user)")
} catch {
print("ā Login failed: \(error)")
}
import PVCheevos
// Login and get client in one step
do {
let (client, session) = try await PVCheevos.login(
username: "MaxMilyin",
password: "mySecurePassword"
)
print("ā
Logged in as \(session.username)")
print("š Total Score: \(session.score ?? 0)")
// Client is ready to use
let awards = try await client.getUserAwards(username: session.username)
print("š
Total Awards: \(awards.totalAwardsCount ?? 0)")
} catch {
print("ā Login failed: \(error)")
}
When using password credentials, the library automatically handles login:
import PVCheevos
// Create client with password credentials
let credentials = RetroCredentials.usernamePassword(
username: "MaxMilyin",
password: "mySecurePassword"
)
let client = PVCheevos.client(credentials: credentials)
// First API call automatically triggers login
let profile = try await client.getUserProfile(username: "MaxMilyin")
print("Profile: \(profile.user)") // Login happened automatically
// Subsequent calls use the cached session
let awards = try await client.getUserAwards(username: "MaxMilyin")
print("Awards: \(awards.totalAwardsCount ?? 0)")
// Check if there's an active session
if let session = await client.getCurrentSession() {
print("Logged in as: \(session.username)")
print("Token: \(session.token)")
print("Score: \(session.score ?? 0)")
} else {
print("Not logged in")
}
// Clear the current session
await client.logout()
print("Logged out successfully")
import PVCheevos
// Create different types of credentials
let apiKeyCredentials = RetroCredentials.webAPIKey(
username: "user",
webAPIKey: "key"
)
let passwordCredentials = RetroCredentials.usernamePassword(
username: "user",
password: "pass"
)
let authenticatedCredentials = RetroCredentials.authenticated(
username: "user",
token: "existing_token"
)
// Use any credential type with the client
let client = PVCheevos.client(credentials: apiKeyCredentials)
import PVCheevos
do {
let (client, session) = try await PVCheevos.login(
username: "user",
password: "wrong_password"
)
} catch let error as RetroError {
switch error {
case .unauthorized:
print("ā Invalid username or password")
case .network(let networkError):
print("š Network error: \(networkError.localizedDescription)")
case .serverError(let message):
print("š„ Server error: \(message ?? "Unknown")")
default:
print("ā ļø Other error: \(error.localizedDescription)")
}
} catch {
print("š„ Unexpected error: \(error)")
}
The CLI tool supports both authentication methods:
# Command line arguments
ra-cli -u your_username -k your_api_key -c profile
# Environment variables
export RA_USERNAME=your_username
export RA_API_KEY=your_api_key
ra-cli -c awards
# Command line arguments
ra-cli -u your_username -p your_password -c profile
# Environment variables
export RA_USERNAME=your_username
export RA_PASSWORD=your_password
ra-cli -c social
import Security
import PVCheevos
class SecureCredentialManager {
private let usernameKey = "ra_username"
private let apiKeyKey = "ra_api_key"
func saveCredentials(username: String, apiKey: String) {
// Save to Keychain (simplified example)
saveToKeychain(key: usernameKey, value: username)
saveToKeychain(key: apiKeyKey, value: apiKey)
}
func loadCredentials() -> RetroCredentials? {
guard let username = loadFromKeychain(key: usernameKey),
let apiKey = loadFromKeychain(key: apiKeyKey) else {
return nil
}
return RetroCredentials.webAPIKey(username: username, webAPIKey: apiKey)
}
// Keychain helper methods (implementation depends on your needs)
private func saveToKeychain(key: String, value: String) { /* ... */ }
private func loadFromKeychain(key: String) -> String? { /* ... */ }
}
// Usage
let credentialManager = SecureCredentialManager()
// Save credentials securely
credentialManager.saveCredentials(username: "user", apiKey: "key")
// Load and use credentials
if let credentials = credentialManager.loadCredentials() {
let client = PVCheevos.client(credentials: credentials)
// Use client...
}
Invalid Credentials Error:
ā Authentication failed - check your credentials
Network Errors:
š Network error: The Internet connection appears to be offline
Session Expiration:
// Test different authentication methods
func testAuthentication() async {
// Test API key
let apiClient = PVCheevos.client(username: "test", webAPIKey: "test_key")
let apiValid = await apiClient.validateCredentials()
print("API Key valid: \(apiValid)")
// Test password login
do {
let (passClient, session) = try await PVCheevos.login(
username: "test",
password: "test_pass"
)
print("Password login successful: \(session.username)")
} catch {
print("Password login failed: \(error)")
}
}
For integration with the Provenance emulator:
class ProvenanceAchievementManager {
private var client: RetroAchievementsClient?
private var currentSession: UserSession?
func authenticateUser(username: String, password: String) async -> Bool {
do {
let (client, session) = try await PVCheevos.login(
username: username,
password: password
)
self.client = client
self.currentSession = session
print("š® Authenticated as \(session.username)")
print("š User has \(session.score ?? 0) points")
return true
} catch {
print("ā Authentication failed: \(error)")
return false
}
}
func loadGameAchievements(gameId: Int) async {
guard let client = client else { return }
do {
let game = try await client.getGame(gameId: gameId)
print("š® Loaded \(game.numAchievements ?? 0) achievements for \(game.title ?? "game")")
// Set up achievement tracking in your emulator core
setupAchievementTracking(achievements: game.achievements)
} catch {
print("ā Failed to load achievements: \(error)")
}
}
private func setupAchievementTracking(achievements: [String: Achievement]?) {
// Implementation depends on your emulator core
// This is where you'd configure the achievement system
}
}
This authentication system provides the flexibility needed for both development and production use cases, supporting both the security of API keys and the user-friendliness of password-based login!