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
- Creating a Service API Class
- Endpoint Definition
- Creating an HTTP request
- Error Handling
- Language Configuration
- Logging
- Android 5.0+ (API level 21+)
- PowerAuth Mobile SDK needs to be implemented in your project
To use the SDK in your Android application include the following dependency to your gradle file.
repositories {
mavenCentral() // if not defined elsewhere...
}
implementation "com.wultra.android.powerauth:powerauth-networking:1.x.y"
WPN SDK | PowerAuth SDK |
---|---|
1.5.x |
1.9.x |
1.4.x |
1.8.x |
1.3.x |
1.8.x |
1.1.x - 1.2.x |
1.7.x |
1.0.x |
1.6.x |
The code of the library is open source and you can freely browse it in our GitHub at https://github.com/wultra/networking-android
Everything you need is packed inside the single com.wultra.android.powerauth.networking.Api
abstract class that provides all the necessary APIs for your networking.
This class takes several parameters:
baseUrl
- Base URL for endpoints. For examplehttps://myservice.com/my-controller/
okHttpClient
- okhttp3 client that will be used for the networking. You can leverage all utilities that are provided by this client like timeout configuration, listeners, etc...powerAuthSDK
-PowerAuthSDK
instance that will sign requestsgsonBuilder
- GsonBuilder for (de)serializationappContext
- Application ContexttokenProvider
- Optional Token Provider in case you have several services and want to share the token logic.userAgent
- Custom user-agent that will be added as an HTTP header to each request.
It is expected that you inherit this class and create your own APIs based on our needs.
Example MyServiceApi that will call 2 sample endpoints (one signed and one signed with token):
class MyServiceApi(
okHttpClient: OkHttpClient,
baseUrl: String,
powerAuthSDK: PowerAuthSDK,
appContext: Context
) : Api(baseUrl, okHttpClient, powerAuthSDK, GsonBuilder(), appContext) {
class SampleRequestData(@SerializedName("uid") val userID: String)
class SampleResponseData(@SerializedName("name") val username: String)
class SampleRequest(requestObject: SampleRequestData): ObjectRequest<SampleRequestData>(requestObject)
class SampleResponse(responseObject: SampleResponseData, status: Status): ObjectResponse<SampleResponseData>(responseObject, status)
companion object {
// This endpoint points to https://my.serviceurl.com/api/auth/token/app/user/sample
private val sampleEndpoint1 = EndpointSigned<SampleRequest, SampleResponse>("api/my/endpoint/user/sample", "/user/get")
// This endpoint points to https://my.serviceurl.com/api/auth/token/app/user/sample2
private val sampleEndpoint2 = EndpointSignedWithToken<SampleRequest, SampleResponse>("api/my/endpoint/user/sample2", "possession_universal")
}
/** Get the username with a token-signed request. */
fun sample1(userID: String, listener: IApiCallResponseListener<SampleResponse>) {
post(SampleRequest(SampleRequestData(userID)), sampleEndpoint1, null, null, null, listener)
}
/** Get the username with a user-signed request. */
fun sample2(userID: String, authentication: PowerAuthAuthentication, listener: IApiCallResponseListener<SampleResponse>) {
post(SampleRequest(SampleRequestData(userID)), sampleEndpoint2, authentication, null, null, null, listener)
}
}
Each endpoint you will target with your project must be defined for the service as an Endpoint
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 constructor. Default value is set to E2EEConfiguration.NOT_ENCRYPTED
.
Possible values are:
/** End to end encryption configuration for an endpoint. */
enum class E2EEConfiguration {
/** Endpoint is encrypted with the application scope. */
APPLICATION_SCOPE,
/** Endpoint is encrypted with the activation scope. */
ACTIVATION_SCOPE,
/** Endpoint is not encrypted. */
NOT_ENCRYPTED
}
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:
val mySignedEndpoint = EndpointSigned<MyRequest, MyResponse>("api/my/endpoint/path", "/endpoint/uriId", E2EEConfiguration.NOT_ENCRYPTED)
// 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:
val myTokenEndpoint = EndpointSignedWithToken<MyRequest, MyResponse>("api/my/endpoint/path", "possession_universal", E2EEConfiguration.NOT_ENCRYPTED)
// token name (`possession_universal` in this case) 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-Android.md#token-based-authentication
For endpoints that are not signed by PowerAuth signature but can be end-to-end encrypted.
Example:
val myBasicEndpoint = EndpointBasic<MyRequest, MyResponse>("api/my/endpoint/path", E2EEConfiguration.NOT_ENCRYPTED)
To create an HTTP request to your endpoint, you need to call the Api.post
method with the following parameters:
data
- with the payload of your requestendpoint
- an endpoint that will be calledauth
-PowerAuthAuthentication
instance that will sign the request- this parameter is missing for the basic and token endpoints
headers
- custom HTTP headers,null
by defaultokHttpInterceptor
- OkHttp interceptor to intercept requests eg. for logging purposes,null
by defaultlistener
- result listener
Example:
// Sample Data that will be sent and received from the server
class SampleRequestData(@SerializedName("uid") val userID: String)
class SampleResponseData(@SerializedName("name") val username: String)
// Request objects
class SampleRequest(requestObject: SampleRequestData): ObjectRequest<SampleRequestData>(requestObject)
class SampleResponse(responseObject: SampleResponseData, status: Status): ObjectResponse<SampleResponseData>(responseObject, status)
// endpoint configuration
val myEndpoint = EndpointSigned<SampleRequest, SampleResponse>("api/my/endpoint/path", "/my/endoint/uriId", E2EEConfiguration.NOT_ENCRYPTED)
// Authentication, for example purposes, expect user PIN 1111
val auth = PowerAuthAuthentication.possessionWithPassword("1111")
// Api.post call
post(
// create request data
SampleRequest(SampleResponseData("12345")),
// specify endpoint
myEndpoint,
// Authenticated with
auth,
// custom HTTP headers
hashMapOf(Pair("MyCustomHeader","Value"))
// no HTTP interceptor
null,
// handle response or error
object : IApiCallResponseListener<SampleResponse> {
override fun onFailure(error: ApiError) {
// handle error
}
override fun onSuccess(result: SampleResponse) {
// handle success
}
}
)
Every error produced by this library is of a ApiError
type. This error contains the following information:
error
- A specific reason, why the error happened. For more information see ApiErrorCode chapter.e
- Original exception/error that caused this error. In case of PowerAuth-related errors, it will be by the type ofApiHttpException
orErrorResponseApiException
Each ApiError
has an optional error
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 |
---|---|
ERROR_GENERIC |
Network error that indicates a generic network issue (for example server internal error). |
POWERAUTH_AUTH_FAIL |
General authentication failure (wrong password, wrong activation state, etc...) |
INVALID_REQUEST |
Invalid request sent - missing request object in the request |
INVALID_ACTIVATION |
Activation is not valid (it is different from configured activation) |
INVALID_APPLICATION |
Invalid application identifier is attempted for operation manipulation. |
INVALID_OPERATION |
Invalid operation identifier is attempted for operation manipulation. |
ERR_ACTIVATION |
Error during activation |
ERR_AUTHENTICATION |
Error in case that PowerAuth authentication fails |
ERR_SECURE_VAULT |
Error during secure vault unlocking |
ERR_ENCRYPTION |
Returned in case encryption or decryption fails |
TOO_MANY_REQUESTS |
Too many same requests |
REMOTE_COMMUNICATION_ERROR |
Communication with remote system failed |
There are many Wultra-specific codes available, each starting with a service prefix:
OPERATION_
- likeOPERATION_EXPIRED
, when operation approval fails because it expired.PUSH_
- likePUSH_REGISTRATION_FAILED
when push registering fails.ACTIVATION_CODE_
- likeACTIVATION_CODE_FAILED
when failing to retrieve the activation code for the ActivationSpawn library.ONBOARDING_
- for onboarding-related errors.IDENTITY_
for identity-related errors.
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.
The library is intensively logging into the console via WPNLogger
.
WPNLogger
calls internally the android.util.Log
class.
You can limit the amount of logged information via verboseLevel
property.
Level | Description |
---|---|
OFF |
Silences all messages. |
ERROR |
Only errors will be printed into the log. |
WARNING (default) |
Errors and warnings will be printed into the log. |
DEBUG |
All messages will be printed into the log. |
The WPNLogger
class offers a static logListener
property. If you provide a listener, all logs will also be passed to it (the library always logs into the Android default log).
Log listener comes in handy when you want to log into a file or some online service.
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 [email protected] 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 [email protected]. Please do not post it to a public issue tracker.