Secured resource access
Resource calls made with the iOS SDK are always secured with certificate pinning. Payload encryption can be enabled in the IDAAS-core. Debug detection can be configured in the Config Model. If enabled, the iOS SDK will verify there is no debugger attached to the app process before fetching the resource. The iOS SDK does not perform any mapping or conversion of the response payload. It is up to the app to process a valid response from the backend.
- APP -> SDK: Instruct the SDK to request a resource.
- SDK -> Resource Gateway: Request a resource at the resource gateway by sending the access token in the request.
- Resource Gateway -> IDAAS-core: Validate the provided token.
- IDAAS-core --> Resource Gateway: Details regarding the access token like scope and user, which will be verified by the resource gateway.
- Resource Gateway -> Resource Server: Get the resource.
- Resource Server --> Resource Gateway: Return the resource.
- Resource Gateway --> SDK: Return the resource.
- SDK --> APP: Resource is returned to the APP which has knowledge of the context of the returned resource.
The iOS SDK can fetch a secure resource in three ways:
- For a resource that is tied to a user (such as account information or transactions), the iOS SDK can execute a secured resource access authenticated request.
- For a resource that is tied to an application but not unique per user, for example application assets, the iOS SDK can execute an anonymous request.
- For a resource that can be fetched without any additional authentication, but for which you would still like to use certificate pinning and jailbreak or debug detection, the iOS SDK can execute an unauthenticated requests.
The iOS SDK does not perform any mapping or conversion of the response payload. It is up to the app to process a valid response from the backend.
Authenticated requests
Secure resource access is performed using UserClient
for authorized calls and DeviceClient
for anonymous calls. Instances can be obtained using userClient
and deviceClient
, or by calling SharedUserClient.instance
and SharedDeviceClient.instance
. Attempting to obtain client instances, without configuring the iOS SDK first, will result in throwing an NSInternalInconsistencyException
exception.
Perform a resource call
For user-specific resources, the user needs to be authenticated and a token that is connected to the authenticated user is presented to the backend when fetching a resource. Fetching user-specific resources using the iOS SDK is done through the following UserClient
method:
@discardableResult
func sendAuthenticatedRequest(_ resourceRequest: ResourceRequest,
completion: @escaping ((ResourceResponse?, Error?) -> Void))
-> NetworkTask?
- (nullable ONGNetworkTask *)fetchResource:(ONGResourceRequest *)request completion:(nullable void (^)(ONGResourceResponse * _Nullable response, NSError * _Nullable error))completion;
Example: authenticated resource call
let request = ONGResourceRequest(path:"/api/users/" method:"GET")
ONGUserClient.sharedInstance().fetchResource(request) { response, error in
// handle response
}
ONGResourceRequest *request = [[ONGResourceRequest alloc] initWithPath:@"/api/users" method:@"GET"];
[[ONGUserClient sharedInstance] fetchResource:request completion:^(ONGResourceResponse * _Nullable response, NSError * _Nullable error) {
// handle response
}];
Implicit requests
Implicit resource requests are performed using the UserClient
. It can be obtained by using the userClient
of the client object or by calling the SharedUserClient.instance
method.
Perform a resource call
Implicit resource calls are authenticated using an implicit access token. It is obtained by the iOS SDK during implicit user authentication. Fetching implicit resources using the iOS SDK is done through the following ONGUserClient
method:
@discardableResult
func sendImplicitRequest(_ resourceRequest: ResourceRequest,
completion: @escaping ((ResourceResponse?, Error?) -> Void))
-> NetworkTask?
- (nullable ONGNetworkTask *)fetchImplicitResource:(ONGResourceRequest *)request completion:(nullable void (^)(ONGResourceResponse *_Nullable response, NSError *_Nullable error))completion;
Example: implicit resource call
let request = ONGResourceRequest(path:"/api/users/" method:"GET")
ONGUserClient.sharedInstance().fetchImplicitResource(request) { response, error in
// handle response
}
ONGResourceRequest *request = [[ONGResourceRequest alloc] initWithPath:@"/api/users" method:@"GET"];
[[ONGUserClient sharedInstance] fetchImplicitResource:request completion:^(ONGResourceResponse * _Nullable response, NSError * _Nullable error) {
// handle response
}];
Anonymous requests
Secure anonymous resource access is performed using the DeviceClient
. It can be obtained by using the deviceClient
of the client object or by calling the SharedDeviceClient.instance
method. Any attempt to obtain the DeviceClient
, without configuring the iOS SDK first, will result in throwing a NSInternalInconsistencyException
exception.
Perform a resource call
For anonymous resources, a token that is connected to this device is presented to the backend. Anonymous resource calls allow you to fetch data that is not user-specific but should not be publicly available to anybody knowing the API.
The iOS SDK requires the device (application) to be registered, valid, and authenticated. Fetching anonymous resources using the iOS SDK is done through the following DeviceClient
method:
@discardableResult
func sendAuthenticatedRequest(_ resourceRequest: ResourceRequest,
completion: @escaping ((ResourceResponse?, Error?) -> Void))
-> NetworkTask?
- (nullable ONGNetworkTask *)fetchResource:(ONGResourceRequest *)request completion:(nullable void (^)(ONGResourceResponse * _Nullable response, NSError * _Nullable error))completion;
Example: anonymous resource call
let request = ONGResourceRequest(path:"/api/users/" method:"GET")
ONGDeviceClient.sharedInstance().fetchResource(request) { response, error in
// handle response
}
ONGResourceRequest *request = [[ONGResourceRequest alloc] initWithPath:@"/api/users" method:@"GET"];
[[ONGDeviceClient sharedInstance] fetchResource:request completion:^(ONGResourceResponse * _Nullable response, NSError * _Nullable error) {
// handle response
}];
Unauthenticated requests
Securing unauthenticated requests is performed using the DeviceClient
. It can be obtained by using the deviceClient
of the client object or by calling the SharedDeviceClient.instance
method.
Perform a resource call
For unauthenticated requests, no token is presented to the backend. Only certificate pinning and jailbreak or debug detection are used when fetching unauthenticated resources. Fetching unauthenticated resources using the iOS SDK is done through the following DeviceClient
method:
@discardableResult
func sendUnauthenticatedRequest(_ resourceRequest: ResourceRequest,
completion: @escaping ((_ response: ResourceResponse?,
_ error: Error?) -> Void)) -> NetworkTask?
- (nullable ONGNetworkTask *)fetchUnauthenticatedResource:(ONGResourceRequest *)request completion:(nullable void (^)(ONGResourceResponse * _Nullable response, NSError * _Nullable error))completion;
Example: unauthenticated resource call
let request = ONGResourceRequest(path:"/api/users/" method:"GET")
ONGDeviceClient.sharedInstance().fetchUnauthenticatedResource(request) { response, error in
// handle response
}
ONGResourceRequest *request = [[ONGResourceRequest alloc] initWithPath:@"/api/users" method:@"GET"];
[[ONGDeviceClient sharedInstance] fetchUnauthenticatedResource:request completion:^(ONGResourceResponse * _Nullable response, NSError * _Nullable error) {
// handle response
}];
Request construction
You can construct requests manually or using the request builder.
Manual request construction
Both authenticated and anonymous resource calls start from the ResourceRequest
construction. The request object combines the following properties:
path
is the URI path, appended to theResourceBaseURL
that is set within the client configuration.method
is the HTTP request method, which can be one of get, post, put, patch, head, and delete.parameters
is a dictionary of parameters used for the request. Parameters are appended to the URL or provided within a body, depending on the request method (respectively get or post).parametersEncoding
is the encoding used for params. The possible values are JSON or FormUrl. The default value is encoding JSON.body
is the raw body of the request. If specified, it overwrites the parameters and parameter encoding.multipartData
is the object that represents the multipart data that should be sent. The multipart or form-data Content-Type header is added automatically. Only the HTTP POST method is supported for a multipart request. If you add multipart data, theparametersEncoding
property is neglected.headers
are additional headers added to the HTTP request. The iOS SDK takes the responsibility for managing theAuthorization
andUser-Agent
headers. These cannot be modified or provided.
Note
The path
can be set to an absolute URL. The domain for a different base URL has to be protected with a valid certificate. If you are using a different domain and there is no certificate, the pinning mechanism will not allow the fetch to be executed.
To create an instance of the ResourceRequest
, you can use ResourceRequestFactory
. In addition to the ResourceRequest
, which is immutable by design, there is a counterpart MutableResourceRequest
that allows you to modify all of the available properties one by one.
public static func makeResourceRequest(path: String,
method: HTTPMethod = .get,
parameters: [String: Any]? = nil,
body: Data? = nil,
headers: [String: String]? = nil,
parametersEncoding: ParametersEncoding = .JSON) -> ResourceRequest {}
public static func makeMultipartResourceRequest(path: String,
method: HTTPMethod,
parameters: [String: Any]?,
multipartData: [MultipartData]) -> ResourceRequest {}
- (instancetype)initWithPath:(NSString *)path method:(NSString *)method;
- (instancetype)initWithPath:(NSString *)path method:(NSString *)method parameters:(nullable NSDictionary<NSString *, id> *)parameters;
- (instancetype)initWithPath:(NSString *)path method:(NSString *)method parameters:(nullable NSDictionary<NSString *, id> *)parameters encoding:(ONGParametersEncoding)encoding;
- (instancetype)initWithPath:(NSString *)path method:(NSString *)method parameters:(nullable NSDictionary<NSString *, id> *)parameters encoding:(ONGParametersEncoding)encoding headers:(nullable NSDictionary<NSString *, NSString *> *)headers;
- (instancetype)initWithPath:(NSString *)path method:(NSString *)method body:(nullable NSData *)body headers:(nullable NSDictionary<NSString *, NSString *> *)headers;
- (instancetype)initWithPath:(NSString *)path method:(NSString *)method parameters:(nullable NSDictionary<NSString *, id> *)parameters multipartData:(NSArray<ONGMultipartData *> *)multipartData;
Example: request construction
let request = ONGResourceRequest(path:"/api/users/" method:"GET" parameters:{"username": "foo@bar.baz"})
// mutable version
let request = ONGMutableRequest(path:@"/api/users/" method:"GET")
request.headers = {"X-Flags": "1000010"}
request.parameters = {"username": "foo@bar.baz"}
request.parametersEncoding = .JSON
ONGResourceRequest *request = [[ONGResourceRequest alloc] initWithPath:@"/api/users/" method:@"GET" parameters:@{@"username": @"foo@bar.baz"}];
// mutable version
ONGMutableRequest *request = [[ONGMutableRequest alloc] initWithPath:@"/api/users/" method:@"GET"];
request.headers = @{@"X-Flags": @"1000010"};
request.parameters = @{@"username": @"foo@bar.baz"};
request.parametersEncoding = ONGParametersEncodingJSON;
Request builder
To simplify request construction, the iOS SDK offers a ONGRequestBuilder
class that allows you to build new immutable requests through a set of methods that reflect the ONGResourceRequest
properties and build an actual request instance by calling the -[ONGRequestBuilder build]
method:
@interface ONGRequestBuilder : NSObject
- (instancetype)setMethod:(NSString *)method;
- (instancetype)setPath:(NSString *)path;
- (instancetype)setParameters:(NSDictionary<NSString *, id> *)parameters;
- (instancetype)setParametersEncoding:(ONGParametersEncoding)parametersEncoding;
- (instancetype)setBody:(NSData *)body;
- (instancetype)setMultipartData:(NSArray<ONGMultipartData *> *)multipartData;
- (instancetype)setHeaders:(NSDictionary<NSString *, NSString *> *)headers;
- (ONGResourceRequest *)build;
@end
Example: request construction using the request builder
let request = ONGRequestBuilder().setMethod("GET").setPath("/api/users").setParameters({"username": "foo@bar.baz"}).build()
ONGResourceRequest *request = [[[[ONGRequestBuilder builder] setMethod:@"GET"] setPath:@"/api/users"] build];
Handling resource responses
The responsibility for handling the fetched resource response belongs to the completion block passed into fetchResource:completion:
.
The completion block will be called whenever a fetch resource call is completed. The following values are provided:
response
is the object containing the status code, headers, and the bare data containing the body that needs to be decoded.error
defines the type of error that occurred. Depending on the error reason, the following domains are possible:ONGFetchResourceErrorDomain
(only for authenticated resource requests),ONGFetchAnonymousResourceErrorDomain
(only for anonymous requests),ONGFetchUnauthenticatedResourceDomain
(only for unauthenticated resource requests), theONGGenericErrorDomain
in case of a generic error, or theNSURLError
in case of an HTTP-related error.
Example: resource call completion
[client fetchResource:request completion:^(ONGResourceResponse * _Nullable response, NSError * _Nullable error) {
// handle the response
}];
Example: resource call implementation with a JSON-encoded payload
client.fetchResource(request) { response, error in
if response?.statusCode < 400, let data = response?.data {
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: [])
// further response handling
} catch {
// response is not JSON encoded
}
} else {
// handle error
}
}
[client fetchResource:request completion:^(ONGResourceResponse * _Nullable response, NSError * _Nullable error) {
if (response && response.statusCode < 400 && response.data) {
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:body options:0 error:nil];
// further mapping of JSON
} else {
// handle error
}
}];
Handling resource call errors
In case of a resource call failure, the iOS SDK will provide an error. Depending on the error reason and resource call type, the following domains are possible: ONGFetchResourceErrorDomain
(only for authenticated requests), ONGFetchAnonymousResourceErrorDomain
(only for anonymous requests), ONGGenericErrorDomain
, NSUrlError
(in case of HTTP-related errors).
Example: error handling
userClient.fetchResource(request) { response, error in
if let error = error {
switch error.code {
case ONGFetchResourceError.UserNotAuthenticated:
// user isn't authenticated
case NSURLError.Cancelled:
// cancelled explicitly, ignore
break
default:
// default error handling
}
} else {
// regular response handling
}
}
[userClient fetchResource:request completion:^(ONGResourceResponse * _Nullable response, NSError * _Nullable error) {
if (error) {
switch(error.code) {
case ONGFetchResourceErrorUserNotAuthenticated:
// user isn't authenticated
break;
case NSURLErrorCancelled:
// cancelled explicitly, ignore
break;
default:
// default error handling
break;
} else {
// regular response handling
}
}
}];
Running resource call handling
Both authenticated and anonymous resource calls return an instance of the ONGNetworkTask
. This object allows you to control and observe the execution by inspecting various properties:
state
- enumONGNetworkTaskState
, describing whether task is running, suspended, cancelled, or completed.identifier
- unique task identifier.request
- request with which the resource call has been made.response
- instance ofONGResourceResponse
. This value is fulfilled upon request completion and contains useful information like HTTP Status Code, response headers, and the actual body of the response. For advanced usage, it provides a reference to theNSHTTPURLResponse
object.error
- instance ofNSError
fulfilled in case of a request failure. It might be of theNSURLErrorDomain
orONGGenericErrorDomain
.
You can control tasks through the following methods:
cancel
- cancel a running or suspended task.suspend
- suspend a running task.resume
- resume a suspended task.