Wultra PowerAuth Networking (WPN) is a high-level SDK built on top of our PowerAuth SDK that enables request signing and encryption.
You can imagine the purpose of this SDK as an HTTP layer (client) that enables request signing and encryption via PowerAuth SDK based on its recommended implementation.
We use this SDK in our other open-source projects that you can take inspiration for example in:
- SDK Integration
- Open Source Code
- Initialization and Configuration
- Endpoint Definition
- Creating an HTTP request
- Raw Response Observer
- Parallel Requests
- SSL validation
- Error Handling
- Language Configuration
- Logging
- iOS 12.0+ and tvOS 12.0+
- PowerAuth Mobile SDK needs to be implemented in your project
Add the https://github.com/wultra/networking-apple
repository as a package in Xcode UI and add the WultraPowerAuthNetworking
library as a dependency.
Alternatively, you can add the dependency manually. For example:
// swift-tools-version:5.9
import PackageDescription
let package = Package(
name: "YourLibrary",
platforms: [
.iOS(.v12)
],
products: [
.library(
name: "YourLibrary",
targets: ["YourLibrary"]
),
],
dependencies: [
.package(url: "https://github.com/wultra/networking-apple.git", .from("1.3.0"))
],
targets: [
.target(
name: "YourLibrary",
dependencies: ["WultraPowerAuthNetworking"]
)
]
)
Add the following dependencies to your Podfile:
pod 'WultraPowerAuthNetworking'
WPN SDK | PowerAuth SDK |
---|---|
1.5.x |
1.9.x |
1.4.x |
1.8.x |
1.3.x |
1.8.x |
1.0.x - 1.2.x |
1.7.x |
We recommend using Xcode version 15.0 or newer.
The code of the library is open source and you can freely browse it in our GitHub at https://github.com/wultra/networking-apple
Everything you need is packed inside the single WPNNetworkingService
class that provides all the necessary APIs for your networking.
To successfully create an instance of the service, you need only 2 things:
- configured
PowerAuthSDK
object - configuration of the service (like endpoints base URL)
You can create as many instances of the class as you need for your usage.
Example:
let networking = WPNNetworkingService(
powerAuth: myPowerAuthInstance, // configured PowerAuthSDK instance
config: WPNConfig(
baseUrl: "https://sandbox.company.com/my-service", // URL to my PowerAuth based service
sslValidation: .default, // use default SSL error handling (more in SSL validation docs section)
timeoutIntervalForRequest: 10, // give 10 seconds for the server to respond
userAgent: .libraryDefault // use library default HTTP User-Agent header
),
serviceName: "MyProjectNetworkingService", // for better debugging
acceptLanguage: "en" // more info in "Language Configuration" docs section
)
Each endpoint you will target with your project must be defined for the service as a WPNEndpoint
instance. There are several types of endpoints based on the PowerAuth signature that is required.
If the endpoint is end-to-end encrypted, you need to configure it in the init. Default initializers are set to e2ee: .notEncrypted
.
Possible values are:
/// Endpoint configuration for end to end encryption.
public enum WPNE2EEConfiguration {
/// Endpoint is encrypted with the application scope.
case applicationScope
/// Endpoint is encrypted with the activation scope.
case activationScope
/// Endpoint is not encrypted.
case notEncrypted
}
Whether an endpoint is encrypted or not is based on its backend definition.
For endpoints that are signed by PowerAuth signature and can be end-to-end encrypted.
Example:
typealias MySignedEndpointType = WPNEndpointSigned<WPNRequest<MyEndpointDataRequest>, WPNResponse<MyEndpointDataResponse>>
var mySignedEndpoint: MySignedEndpointType { WPNEndpointSigned(endpointURLPath: "/additional/path/to/the/signed/endpoint", uriId: "endpoint/identifier", e2ee: .notEncrypted) }
// uriId is defined by the endpoint issuer - ask your server developer/provider
For endpoints that are signed by token by PowerAuth signature and can be end-to-end encrypted.
More info for token-based authentication can be found here
Example:
typealias MyTokenEndpointType = WPNEndpointSignedWithToken<WPNRequest<MyEndpointDataRequest>, WPNResponse<MyEndpointDataResponse>>
var myTokenEndpoint: MyTokenEndpointType { WPNEndpointSignedWithToken(endpointURLPath: "/additional/path/to/the/token/signed/endpoint", tokenName: "MyToken", e2ee: .notEncrypted) }
// tokenName is the name of the token as stored in the PowerAuthSDK
// more info can be found in the PowerAuthSDK documentation
// https://github.com/wultra/powerauth-mobile-sdk/blob/develop/docs/PowerAuth-SDK-for-iOS.md#token-based-authentication
For endpoints that are not signed by PowerAuth signature but can be end-to-end encrypted.
Example:
typealias MyBasicEndpointType = WPNEndpointBasic<WPNRequest<MyEndpointDataRequest>, WPNResponse<MyEndpointDataResponse>>
var myBasicEndpoint: MyBasicEndpointType { WPNEndpointBasic(endpointURLPath: "/additional/path/to/the/basic/endpoint", e2ee: .notEncrypted) }
To create an HTTP request to your endpoint, you need to call the WPNNetworkingService.post
method with the following parameters:
data
- with the payload of your requestauth
-PowerAuthAuthentication
instance that will sign the request- this parameter is missing for the basic endpoint
endpoint
- an endpoint that will be calledheaders
- custom HTTP headers,nil
by defaulttimeoutInterval
- timeout interval,nil
by default. Whennil
, the default configured inWPNConfig
will be usedprogressCallback
- callback with percentage progress (values between 0 and 1)completionQueue
- queue that the completion will be called on (main queue by default)completion
- result completion
Example:
// payload we will send to the server
struct MyRequestPayload {
let userID: String
}
// response of the server
struct MyResponse {
let name: String
let email: String
}
// endpoint configuration
typealias MyEndpointType = WPNEndpointSigned<WPNRequest<MyRequestPayload>, WPNResponse<MyResponse>>
var endpoint: MyEndpointType { WPNEndpointSigned(endpointURLPath: "/path/to/myendpoint", uriId: "myendpoint/identifier") }
// Authentication (for example purposes) expect user PIN 1111
let auth = PowerAuthAuthentication.possessionWithPassword("1111")
// WPNNetworkingService instance call
networking.post(
// create request data
data: MyEndpointType.RequestData(.init(userID: "12345")),
// specify endpoint
to: endpoint,
// custom HTTP headers
with: ["MyCustomHeader": "Value"],
// only wait 10 seconds at max
timeoutInterval: 10,
// handle response or error
completion: { result, error in
if let data = result?.responseObject {
// we have data
} else {
// handle error or empty response
}
}
)
We use systems URLSession
under the hood.
All responses can be observed with WPNResponseDelegate
in WPNNetworkingService.responseDelegate
.
An example implementation of the delegate:
class MyResponseDelegateLogger: WPNResponseDelegate {
func responseReceived(from url: URL, statusCode: Int?, body: Data) {
print("Response received from \(url) with status code \(statusCode) and data:")
print(String(data: body, encoding: .utf8) ?? "")
}
// for endpoints that are end-to-end encrypted
func encryptedResponseReceived(from url: URL, statusCode: Int?, body: Data, decrypted: Data) {
print("Encrypted response received from \(url) with status code \(statusCode) and: ")
print(" Raw data:")
print(String(data: body, encoding: .utf8) ?? "")
print(" Decrypted data:")
print(String(data: decrypted, encoding: .utf8) ?? "")
}
}
By default, the SDK is serializing all signed requests. This means that the requests signed with the PowerAuthSDK are put into the queue and executed one by one (meaning that the HTTP request is not made until the previous one is finished). Other requests will be parallel.
This behavior can be changed via WPNNetworkingService.concurrencyStrategy
with the following possible values:
serialSigned
- Default behavior. Only requests that need a PowerAuth signature will be put into the serial queue that is shared with thePowerAuthSDK
instance to ensure all signed requests are in proper order.concurrentAll
- All requests will be put into the concurrent queue. This behavior is not recommended unless you know exactly why you want this.
More about this topic can be found in the PowerAuth documentation.
The SDK uses default system handling of the SSL errors. To be able to ignore SSL errors (for example when your test server does not have a valid SSL certificate) or implement your own SSL pinning, you can configure WPNConfig.sslValidation
property to get your desired behavior.
Possible values are:
default
- Uses default URLSession handling.noValidation
- Trust HTTPS connections with invalid certificates.sslPinning(_ provider: WPNPinningProvider)
- Validates the server certificate with your own logic.
Every error produced by this library is of a WPNError
type. This error contains the following information:
reason
- A specific reason, why the error happened. For more information see WPNErrorReason chapter.nestedError
- Original exception/error (if available) that caused this error.httpStatusCode
- If the error is a networking error, this property will provide the HTTP status code of the error.httpUrlResponse
- If the error is a networking error, this will hold the original HTTP response that was received from the backend.restApiError
- If the error is a "well-known" API error, it will be filled here. For all available codes follow the source code.networkIsNotReachable
- Convenience property, informs about a state where the network is unavailable (based on the error type).networkConnectionIsNotTrusted
- Convenience property, informs about a TLS error.powerAuthErrorResponse
- If the error was caused by the PowerAuth error, you can retrieve it here.powerAuthRestApiErrorCode
- If the error was caused by the PowerAuth error, the error code of the original error will be available here.
Each WPNError
has a reason
property for why the error was created. Such reason can be useful when you're creating for example a general error handling or reporting, or when you're debugging the code.
Option Name | Description |
---|---|
unknown |
Unknown fallback reason |
missingActivation |
PowerAuth instance is missing an activation. |
Option Name | Description |
---|---|
network_unknown |
When unknown (usually logic error) happened during networking. |
network_generic |
Network error that indicates a generic network issue (for example server internal error). |
network_errorStatusCode |
HTTP response code was different than 200 (success). |
network_invalidResponseObject |
An unexpected response from the server. |
network_invalidRequestObject |
Request is not valid. Such an object is not sent to the server. |
network_signError |
When the signing of the request failed. |
network_timeOut |
Request timed out |
network_noInternetConnection |
Not connected to the internet. |
network_badServerResponse |
Bad (malformed) HTTP server response. Probably an unexpected HTTP server error. |
network_sslError |
SSL error. For detailed information, see the attached error object when available. |
WPNErrorReason
is a struct that can be created by other libraries so the list above is not a final list of all possible errors. Such errors (in libraries developed by Wultra) will be presented in the dedicated documentation (for example Mobile Token SDK library).
Before using any methods from this SDK that call the backend, a proper language should be set. A properly translated content is served based on this configuration. The property that stores language settings does not persist. You need to set acceptLanguage
every time that the application boots.
Note: Content language capabilities are limited by the implementation of the server - it must support the provided language.
The default value is always en
. With other languages, we use values compliant with standard RFC Accept-Language.
You can set up logging for the library using the WPNLogger
class.
You can limit the amount of logged information via the verboseLevel
property.
Level | Description |
---|---|
off |
Silences all logs. |
errors |
Only errors will be logged. |
warnings (default) |
Errors and warnings will be logged. |
info |
Error, warning and info messages will be logged. |
debug |
All messages will be logged. |
To prevent huge logs from being printed out, there is a default limit of 12,000 characters per log in place. You can change this via WPNLogger.characterLimit
.
- You can turn on or off logging of HTTP requests and responses with the
WPNLogger.logHttpTraffic
property. - You can filter which headers will be logged with the
WPNLogger.httpHeadersToSkip
property.
In case you want to process logs on your own (for example log into a file or some cloud service), you can set WPNLogger.delegate
.
This documentation is also available at the Wultra Developer Portal.
All sources are licensed using the Apache 2.0 license. You can use them with no restrictions. If you are using this library, please let us know. We will be happy to share and promote your project.
If you need any assistance, do not hesitate to drop us a line at hello@wultra.com or our official wultra.com/discord channel.
If you believe you have identified a security vulnerability with this SDK, you should report it as soon as possible via email to support@wultra.com. Please do not post it to a public issue tracker.