Manos Chorianopoulos

add login request

......@@ -731,7 +731,73 @@ let networkStatus = WarplySDK.shared.getNetworkStatus()
## 🔐 Authentication
### User Login
### DEI Login (Email-based Authentication) 🆕
The DEI login method provides email-based authentication for DEI platform users. This method automatically handles JWT token extraction and secure storage.
```swift
// Completion handler approach
WarplySDK.shared.deiLogin(
email: "user@example.com",
completion: { response in
if let response = response, response.getStatus == 1 {
print("DEI login successful")
// User is now authenticated - proceed with authenticated operations
// Tokens are automatically stored and managed by the SDK
} else {
print("DEI login failed")
}
},
failureCallback: { errorCode in
print("DEI login failed with error: \(errorCode)")
// Handle specific error codes
switch errorCode {
case 401:
print("Invalid email or authentication failed")
case -1009:
print("No internet connection")
default:
print("Unknown error occurred")
}
}
)
// Async/await approach (Recommended)
Task {
do {
let response = try await WarplySDK.shared.deiLogin(email: "user@example.com")
if response.getStatus == 1 {
print("DEI login successful")
// User is now authenticated - proceed with authenticated operations
// Access other authenticated endpoints like getCoupons, getProfile, etc.
} else {
print("DEI login failed")
}
} catch let error as WarplyError {
switch error {
case .authenticationFailed:
print("Invalid email or authentication failed")
case .networkError:
print("Network error occurred")
case .noInternetConnection:
print("No internet connection")
default:
print("DEI login error: \(error.localizedDescription)")
}
} catch {
print("Unexpected error: \(error)")
}
}
```
**Key Features:**
- ✅ **Email-based authentication** - No complex credentials required
- ✅ **Automatic token management** - JWT tokens extracted and stored securely
- ✅ **Future-ready design** - Handles refresh tokens (currently null, will be valid later)
- ✅ **Comprehensive error handling** - Detailed error codes and messages
- ✅ **Analytics integration** - Success/failure events tracked automatically
### Traditional User Login
```swift
// Completion handler approach
......@@ -757,6 +823,32 @@ Task {
}
```
### Cosmote User Authentication
```swift
// For Cosmote-specific authentication
WarplySDK.shared.getCosmoteUser(guid: "cosmote-user-guid") { response in
if let response = response, response.getStatus == 1 {
print("Cosmote user authenticated")
// Tokens automatically stored
} else {
print("Cosmote authentication failed")
}
}
// Async/await variant
Task {
do {
let response = try await WarplySDK.shared.getCosmoteUser(guid: "cosmote-user-guid")
if response.getStatus == 1 {
print("Cosmote user authenticated")
}
} catch {
print("Cosmote authentication error: \(error)")
}
}
```
### User Logout
```swift
......
# DEI Login Implementation Test
## Implementation Summary
The DEI login functionality has been successfully implemented with the following components:
### 1. Endpoint Configuration (Endpoints.swift)
- ✅ Added `deiLogin(email: String)` case to Endpoint enum
- ✅ Configured path: `/partners/dei/app_login`
- ✅ Configured method: POST
- ✅ Configured parameters: `["email": email]`
- ✅ Configured authentication: `.standard` (no authorization header)
- ✅ Set `requiresAuthentication: false`
### 2. Network Service Method (NetworkService.swift)
- ✅ Added `deiLogin(email: String)` method in NetworkService
- ✅ Implemented comprehensive error handling
- ✅ Added response validation (status == 1)
- ✅ Added token extraction and validation
- ✅ Added automatic TokenModel creation and database storage
- ✅ Added detailed logging for debugging
### 3. Public SDK Methods (WarplySDK.swift)
- ✅ Added completion handler variant: `deiLogin(email:completion:failureCallback:)`
- ✅ Added async/await variant: `deiLogin(email:) async throws`
- ✅ Added comprehensive documentation with examples
- ✅ Added analytics events for success/failure tracking
- ✅ Added state management (clears CCMS campaigns)
- ✅ Added proper error handling and conversion
## Key Features Implemented
### Authentication Flow
1. **Email-based authentication** - No authorization header required
2. **JWT token extraction** - Extracts access_token and refresh_token from nested response
3. **Automatic token storage** - Stores tokens in database using TokenModel
4. **Future-ready design** - Handles null refresh tokens (will be valid later)
### Error Handling
1. **Status validation** - Ensures response status == 1
2. **Token validation** - Ensures access_token exists and is not empty
3. **Network error handling** - Comprehensive error mapping and logging
4. **Analytics tracking** - Success/failure events for monitoring
### Response Structure Support
Expected response format:
```json
{
"status": 1,
"tokens": {
"access_token": "eyJ...",
"refresh_token": null,
"client_id": "...",
"client_secret": "..."
}
}
```
## Usage Examples
### Completion Handler Variant
```swift
WarplySDK.shared.deiLogin(
email: "user@example.com",
completion: { response in
if let response = response, response.getStatus == 1 {
print("DEI login successful")
// User is now authenticated - proceed with authenticated operations
} else {
print("DEI login failed")
}
},
failureCallback: { errorCode in
print("DEI login failed with error: \(errorCode)")
}
)
```
### Async/Await Variant
```swift
Task {
do {
let response = try await WarplySDK.shared.deiLogin(email: "user@example.com")
if response.getStatus == 1 {
print("DEI login successful")
// User is now authenticated - proceed with authenticated operations
} else {
print("DEI login failed")
}
} catch {
print("DEI login failed with error: \(error)")
}
}
```
## Integration with Existing Framework
### Token Management
- ✅ Integrates with existing TokenModel system
- ✅ Integrates with DatabaseManager for secure storage
- ✅ Integrates with TokenRefreshManager for future token refresh
- ✅ Integrates with NetworkService for automatic token usage
### Analytics
- ✅ Follows existing analytics patterns
- ✅ Posts success/failure events to Dynatrace
- ✅ Uses existing event system (SwiftEventBus + EventDispatcher)
### Error Handling
- ✅ Uses existing WarplyError system
- ✅ Follows existing error mapping patterns
- ✅ Provides consistent error codes and messages
## Testing Checklist
### Basic Functionality
- [ ] Test successful login with valid email
- [ ] Test failed login with invalid email
- [ ] Test network error handling
- [ ] Test token extraction and storage
- [ ] Test both completion handler and async/await variants
### Integration Testing
- [ ] Test that tokens are stored in database
- [ ] Test that tokens are used for subsequent authenticated requests
- [ ] Test analytics events are posted correctly
- [ ] Test error handling and conversion
### Edge Cases
- [ ] Test with malformed response
- [ ] Test with missing tokens in response
- [ ] Test with null refresh token (current expected behavior)
- [ ] Test network connectivity issues
## Implementation Status: ✅ COMPLETE
All required components have been implemented:
1. ✅ Endpoint configuration
2. ✅ Network service method
3. ✅ Public SDK methods (both variants)
4. ✅ Comprehensive documentation
5. ✅ Error handling and analytics
6. ✅ Integration with existing systems
The DEI login functionality is ready for testing and integration.
This diff is collapsed. Click to expand it.
......@@ -2840,6 +2840,159 @@ public final class WarplySDK {
}
}
// MARK: - DEI Authentication
/**
* DEI Login with automatic token handling
*
* This method authenticates a user with the DEI platform using their email address.
* Upon successful authentication, it automatically extracts and stores JWT tokens
* in the database for future authenticated requests.
*
* @param email User's email address for DEI authentication
* @param completion Completion handler with response model (nil on failure)
* @param failureCallback Failure callback with error code
*
* @discussion This method performs:
* - Email-based authentication with DEI platform
* - Automatic JWT token extraction and validation
* - Secure token storage in database using TokenModel
* - Comprehensive error handling and analytics
*
* @note The DEI login endpoint does not require authorization headers.
* Tokens are automatically stored and will be used by NetworkService for future requests.
*
* Error Scenarios:
* - Invalid email format: Server validation error
* - Network issues: Network error callback
* - Invalid response structure: Parsing error
* - Missing tokens: Authentication error
*
* @example
* ```swift
* WarplySDK.shared.deiLogin(
* email: "user@example.com",
* completion: { response in
* if let response = response, response.getStatus == 1 {
* print("DEI login successful")
* // User is now authenticated - proceed with authenticated operations
* } else {
* print("DEI login failed")
* }
* },
* failureCallback: { errorCode in
* print("DEI login failed with error: \(errorCode)")
* }
* )
* ```
*/
public func deiLogin(email: String, completion: @escaping (VerifyTicketResponseModel?) -> Void, failureCallback: @escaping (Int) -> Void) {
// Clear previous state
setCCMSLoyaltyCampaigns(campaigns: [])
Task {
do {
let response = try await networkService.deiLogin(email: email)
let tempResponse = VerifyTicketResponseModel(dictionary: response)
await MainActor.run {
if tempResponse.getStatus == 1 {
print("✅ [WarplySDK] DEI login successful")
// Analytics for successful login
let dynatraceEvent = LoyaltySDKDynatraceEventModel()
dynatraceEvent._eventName = "custom_success_dei_login_loyalty"
dynatraceEvent._parameters = nil
self.postFrameworkEvent("dynatrace", sender: dynatraceEvent)
// Tokens are already extracted and stored by NetworkService.deiLogin
print("✅ [WarplySDK] DEI tokens automatically stored by NetworkService")
} else {
print("❌ [WarplySDK] DEI login failed - invalid status")
// Analytics for failed login
let dynatraceEvent = LoyaltySDKDynatraceEventModel()
dynatraceEvent._eventName = "custom_error_dei_login_loyalty"
dynatraceEvent._parameters = nil
self.postFrameworkEvent("dynatrace", sender: dynatraceEvent)
}
completion(tempResponse)
}
} catch {
await MainActor.run {
print("❌ [WarplySDK] DEI login network error: \(error)")
// Analytics for network error
let dynatraceEvent = LoyaltySDKDynatraceEventModel()
dynatraceEvent._eventName = "custom_error_dei_login_loyalty"
dynatraceEvent._parameters = nil
self.postFrameworkEvent("dynatrace", sender: dynatraceEvent)
self.handleError(error, context: "deiLogin", endpoint: "deiLogin", failureCallback: failureCallback)
}
}
}
}
/**
* DEI Login with automatic token handling (async/await variant)
*
* This method authenticates a user with the DEI platform using their email address.
* Upon successful authentication, it automatically extracts and stores JWT tokens
* in the database for future authenticated requests.
*
* @param email User's email address for DEI authentication
* @returns VerifyTicketResponseModel containing authentication result
* @throws WarplyError if the request fails
*
* @discussion This method performs:
* - Email-based authentication with DEI platform
* - Automatic JWT token extraction and validation
* - Secure token storage in database using TokenModel
* - Comprehensive error handling and analytics
*
* @note The DEI login endpoint does not require authorization headers.
* Tokens are automatically stored and will be used by NetworkService for future requests.
*
* Error Scenarios:
* - Invalid email format: Server validation error
* - Network issues: WarplyError.networkError
* - Invalid response structure: WarplyError.dataParsingError
* - Missing tokens: WarplyError.authenticationFailed
*
* @example
* ```swift
* Task {
* do {
* let response = try await WarplySDK.shared.deiLogin(email: "user@example.com")
* if response.getStatus == 1 {
* print("DEI login successful")
* // User is now authenticated - proceed with authenticated operations
* } else {
* print("DEI login failed")
* }
* } catch {
* print("DEI login failed with error: \(error)")
* }
* }
* ```
*/
public func deiLogin(email: String) async throws -> VerifyTicketResponseModel {
return try await withCheckedThrowingContinuation { continuation in
deiLogin(email: email, completion: { response in
if let response = response {
continuation.resume(returning: response)
} else {
continuation.resume(throwing: WarplyError.authenticationFailed)
}
}, failureCallback: { errorCode in
continuation.resume(throwing: WarplyError.unknownError(errorCode))
})
}
}
// MARK: - Push Notifications
/// Handle notification
......
......@@ -57,6 +57,7 @@ public enum Endpoint {
case refreshToken(clientId: String, clientSecret: String, refreshToken: String)
case logout
case getCosmoteUser(guid: String)
case deiLogin(email: String)
// Campaigns
case getCampaigns(language: String, filters: [String: Any])
......@@ -122,6 +123,8 @@ public enum Endpoint {
return "/partners/cosmote/verify"
case .getCosmoteUser:
return "/partners/oauth/{appUUID}/token"
case .deiLogin:
return "/partners/dei/app_login"
// Authentication endpoints
case .refreshToken:
......@@ -163,7 +166,7 @@ public enum Endpoint {
switch self {
case .register, .changePassword, .resetPassword, .requestOtp, .verifyTicket, .refreshToken, .logout, .getCampaigns, .getCampaignsPersonalized,
.getCoupons, .getCouponSets, .getAvailableCoupons,
.getMarketPassDetails, .getProfile, .addCard, .getCards, .deleteCard, .getTransactionHistory, .getPointsHistory, .validateCoupon, .redeemCoupon, .getMerchants, .getMerchantCategories, .getArticles, .sendEvent, .sendDeviceInfo, .getCosmoteUser:
.getMarketPassDetails, .getProfile, .addCard, .getCards, .deleteCard, .getTransactionHistory, .getPointsHistory, .validateCoupon, .redeemCoupon, .getMerchants, .getMerchantCategories, .getArticles, .sendEvent, .sendDeviceInfo, .getCosmoteUser, .deiLogin:
return .POST
case .getSingleCampaign, .getNetworkStatus:
return .GET
......@@ -224,6 +227,12 @@ public enum Endpoint {
"user_identifier": guid
]
// DEI Login - simple email parameter structure
case .deiLogin(let email):
return [
"email": email
]
// Campaign endpoints - nested structure with campaigns wrapper
case .getCampaigns(let language, let filters):
return [
......@@ -447,7 +456,7 @@ public enum Endpoint {
public var requiresAuthentication: Bool {
switch self {
case .register, .verifyTicket, .getCosmoteUser, .getNetworkStatus:
case .register, .verifyTicket, .getCosmoteUser, .deiLogin, .getNetworkStatus:
return false
default:
return true
......@@ -505,7 +514,7 @@ public enum Endpoint {
// Standard Authentication (loyalty headers only)
case .register, .resetPassword, .requestOtp, .getCampaigns, .getAvailableCoupons, .getCouponSets, .refreshToken, .logout,
.verifyTicket, .getSingleCampaign, .sendEvent, .sendDeviceInfo,
.getMerchants, .getMerchantCategories, .getArticles, .getNetworkStatus:
.getMerchants, .getMerchantCategories, .getArticles, .getNetworkStatus, .deiLogin:
return .standard
// Bearer Token Authentication (loyalty headers + Authorization: Bearer)
......
......@@ -962,6 +962,57 @@ extension NetworkService {
return response
}
// MARK: - DEI Authentication Methods
/// DEI Login with automatic token handling
/// - Parameter email: User's email address for DEI authentication
/// - Returns: Response dictionary containing authentication result
/// - Throws: NetworkError if request fails
public func deiLogin(email: String) async throws -> [String: Any] {
print("🔄 [NetworkService] DEI Login for email: \(email)")
let endpoint = Endpoint.deiLogin(email: email)
let response = try await requestRaw(endpoint)
// Validate response status
guard let status = response["status"] as? Int, status == 1 else {
print("❌ [NetworkService] DEI Login failed - invalid status")
throw NetworkError.serverError(400)
}
// Extract and validate tokens
guard let tokens = response["tokens"] as? [String: Any],
let accessToken = tokens["access_token"] as? String,
!accessToken.isEmpty else {
print("❌ [NetworkService] DEI Login failed - missing or empty access_token")
throw NetworkError.invalidResponse
}
// Extract refresh token (may be null initially but prepare for future)
let refreshToken = tokens["refresh_token"] as? String
print("✅ [NetworkService] DEI Login successful - tokens received")
print(" Access token: \(accessToken.prefix(10))...")
print(" Refresh token: \(refreshToken?.prefix(10) ?? "null")...")
// Create TokenModel and store in database
let tokenModel = TokenModel(
accessToken: accessToken,
refreshToken: refreshToken, // May be nil/null initially
clientId: tokens["client_id"] as? String,
clientSecret: tokens["client_secret"] as? String
)
do {
try await DatabaseManager.shared.storeTokenModel(tokenModel)
print("✅ [NetworkService] DEI tokens stored in database successfully")
} catch {
print("❌ [NetworkService] Failed to store DEI tokens in database: \(error)")
// Don't throw - the login was successful, token storage is secondary
}
return response
}
// MARK: - Merchant Categories Methods
/// Get merchant categories
......