iOS Civic App Connect
Civic App Connect is a library that allows a third party to connect with the Civic Secure Identity iOS app.
Introduction
This library allows you to connect to the Civic Secure Identity iOS application using the integration portal API. In order to use this library you will need to register yourself on the Integration Portal. Once registered you'll be able to provide the necessary information to the library to be able to connect your own iOS application to the Civic Secure Identity iOS application. The library also provides a Civic styled button that can be customized for your needs, or you could rather initiate the flow in any way you see fit for your application.
Requirements
- iOS 8.0+
- Xcode 10+
- Swift 4+
Installation
Cocoapods
Civic App Connect is available through CocoaPods. To install it, simply add the following line to your Podfile:
pod 'CivicConnect'
Carthage
To integrate Civic App Connect into your Xcode project using Carthage, specify it in your Cartfile
:
github "civicteam/civic-connect-ios-public"
Run carthage update
to build the framework and drag the built CivicConnect.framework into your Xcode project.
Getting Started
Sample App
To run the example project, clone the repo, and run pod install
. Open up CivicConnect.xcworkspace
with Xcode, in the Info.plist
you will need to provide your CivicApplicationIdentifier
and CivicSecret
and run the CivicConnect-Example
scheme. The sample project contains documented code to illustrate how to use the library.
Usage
This library is supported for both Objective-C and Swift and therefore each code snippet will contain its corresponding Objective-C and Swift section.
Initialization
Before being able to use the library, you will need to initialize it with the correct fields These values can be found on the Integration Portal when you have registered your application. Initializing the library requires the following fields:
Name | Required | Description |
---|---|---|
Application Identifier | Yes | Identifier used to identify the third party. |
Mobile Application Identifier | Yes | Identifier used by the mobile app. |
Secret | No | Secret provided to the partner via the Integration Portal. Optional if you just require the JWT token. |
Redirect Scheme | No | Scheme used to open the third party app. (This requires the third party to add a URL Types scheme in the Info.plist file. |
Swift
let connect = Connect(applicationIdentifier: <INSERT APPLICATION IDENTIFIER HERE>,
mobileApplicationIdentifier: <INSERT MOBILE APPLICATION IDENTIFIER HERE>,
secret: <INSERT SECRET HERE>,
redirectScheme: <INSERT REDIRECT SCHEME HERE>)
Objective-C
CCConnect *connect = [[CCConnect alloc] initWithApplicationIdentifier:<INSERT APPLICATION IDENTIFIER HERE>
mobileApplicationIdentifier:<INSERT MOBILE APPLICATION IDENTIFIER HERE>
secret:<INSERT SECRET HERE>
redirectScheme:<INSERT REDIRECT SCHEME HERE>];
A convenient way to initialize the library is to load the fields via the Info.plist
:
Swift
let connect = try Connect.initialize(withBundle: Bundle.main, secret: <INSERT SECRET HERE>)
Objective-C
NSError *error;
NSBundle *bundle = [NSBundle mainBundle];
CCConnect *connect = [CCConnect initializeWithBundle:bundle secret:<INSERT SECRET HERE> error:&error];
The library will look through the Info.plist
to find the following fields:
Name | Type | Required |
---|---|---|
CFBundleIdentifier (Bundle Identifier - By default this should already exist) | String |
Yes |
CivicApplicationIdentifier | String |
Yes |
CivicSecret *deprecated | String |
Yes |
CivicRedirectScheme | String |
No |
Please note: CivicRedirectScheme
requires you to add a URL Type
to the Info.plist
with an identifier and at least one scheme. The scheme needs to be equal to the CivicRedirectScheme
field.
Loading the fields from the Info.plist
can throw the following errors if it cannot find the particular field:
Error | Status Code | Message |
---|---|---|
cannotFindApplicationId | 901 | Cannot find application ID. Make sure you have 'CivicApplicationIdentifier' somewhere in your Info.plist. |
cannotFindBundleId | 902 | Cannot find bundle ID. Make sure you have 'CFBundleIdentifier' somewhere in your Info.plist. |
cannotFindSecret | 903 | Cannot find secret. Make sure you have 'CivicSecret' somewhere in your Info.plist. |
redirectSchemeMismatch | 904 | Cannot find a matching URL scheme for 'CivicRedirectScheme'. Please ensure 'CivicRedirectScheme' matches one of the 'CFBundleURLTypes' 'CFBundleURLSchemes'. |
Connecting
Once you have an initialized instance of the Connect
class, you'll be able to start the connection between your application and Civic. To initiate a flow, you can use the Connect.connect(withType:delegate:)
method:
Swift
connect.connect(withType: .basic, delegate: self)
Objective-C
[connect connectWithType:CCScopeRequestTypeBasic delegate:self];
To determine what type to use, you'll need to know what information you require. The following table shows what information the types provide:
Scope Request Type | Description |
---|---|
ScopeRequestType.basicSignup |
Includes basic information such as email and phone number . |
ScopeRequestType.anonymousLogin |
Only includes the user ID. |
ScopeRequestType.proofOfResidence |
Includes the basic information, identity document and residential documents of the user. |
ScopeRequestType.proofOfIdentity |
Includes basic information such as email and phone number as well as information on an identity document . |
ScopeRequestType.proofOfAge |
Includes the age of the user. |
The delegate is the way the library communicates back to the partner via the following methods:
Method | Description |
---|---|
func connectDidFailWithError(_ error: ConnectError) |
This method is fired off when an error occurs inside the ConnectSession due to service errors, session errors, etc. |
func connectDidFinishWithUserId(_ userId: String, andUserInfo userInfo: [UserInfo]) |
This method is fired off when the ConnectSession has retrieved the user data from the servers. |
func connectDidChangeStatus(_ newStatus: ConnectStatus) |
This method is fired off when the state of the ConnectSession changes. It provides an easy way to know what is happening in the background. |
func connectShouldFetchUserData(withToken token: String) -> Bool |
This method is fired off when the ConnectSession receives the JWT token from the server. At this point we ask the delegate whether we should continue to fetch the user data using the JWT token. Returning true will allow the ConnectSession to retrieve the user data, while false ends the session. For Swift this method is implmemented by default and returns true through the use of extensions. |
After connecting with Civic, a ConnectSession
is created to handle the connection between the partner app and Civic. Once the ConnectSession
is created, you have two ways of handling the session. Either handling the URL
that was used to open the partner app via the Connect.handle(url:)
method or by calling the Connect.startPollingForUserData()
method. These methods will be described below.
URL
Handling Handling a URL
will allow the library to determine whether the Civic application opened up/redirected to the partner application and start retrieving the user data. In order for this feature to work, you must setup a URL Type
with a URL Scheme
in the Info.plist
. (See the example project to see how this is done)
A convenient place to handle the URL
is in the AppDelegate
on the func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool
method as follows:
Swift
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
return connect.handle(url: url)
}
Objective-C
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(nullable NSString *)sourceApplication annotation:(id)annotation {
return [connect handleUrl:url];
}
Internally this will check if the URL
was initiated from the Civic application. You can manually check the URL
via Connect.canHandle(url:)
. If the session can handle the URL
then it will call Connect.startPollingForUserData()
internally.
Start Polling For User Data
If you do not choose to handle the URL
method or you would prefer not to use the redirect mechanism, you can rather choose to start polling for the user data manually. When calling this method, it will initiate a timer that contantly checks when the Civic application has finished the flow.
Swift
connect.startPollingForUserData()
Objective-C
[connect startPollingForUserData];
Stop Polling For User Data
At anytime the session is started, you can call Connect.stopPollingForUserData()
to invalidate the timer.
Swift
connect.stopPollingForUserData()
Objective-C
[connect stopPollingForUserData];
Reset
You can reset the status of the of Connect
instance by calling the Connect.reset()
function.
Swift
connect.reset()
Objective-C
[connect reset];
Connect Button
If you would like a Civic styled button, you can use the ConnectButton
class which is just a subclass of UIButton
. In order to create one, you will need to provide a Connect
instance, a ConnectDelegate
and optionally the ScopeRequestType
:
Swift
let connectButton = ConnectButton(<INSERT CONNECT HERE>,
type: <INSERT SCOPE REQUEST TYPE HERE (DEFAULTS TO .basic)>,
delegate: <INSERT CONNECT DELEGATE HERE>)
Objective-C
CCConnectButton *connectButton = [[CCConnectButton alloc] initWithConnect:<INSERT CONNECT HERE>
type:<INSERT SCOPE REQUEST TYPE HERE>
delegate:<INSERT CONNECT DELEGATE HERE>];
Once created, you can customize the title and image of the button:
Swift
connectButton.setConnectTitle(<INSERT TITLE HERE>, image: <INSERT IMAGE HERE>)
connectButton.setConnectTitle(<INSERT TITLE HERE>)
Objective-C
[connectButton setConnectTitle:<INSERT TITLE HERE> image:<INSERT IMAGE HERE>];
[connectButton setConnectTitle:<INSERT TITLE HERE>];
The scope request type can also be changed at anytime:
Swift
connectButton.setType(<INSERT SCOPE REQUEST TYPE HERE>)
Objective-C
[connectButton setType:<INSERT SCOPE REQUEST TYPE HERE>];
Anytime the ConnectButton
is tapped, it will start connecting with the Civic application and display a loading indicator on the button. The loading indicator will disappear once the session has received either an error or success response.
Errors
The following table shows the potential errors that can occur via the library:
Error | Status Code | Message |
---|---|---|
cannotFindApplicationId | 901 | Cannot find application ID. Make sure you have 'CivicApplicationIdentifier' somewhere in your Info.plist. |
cannotFindBundleId | 902 | Cannot find bundle ID. Make sure you have 'CFBundleIdentifier' somewhere in your Info.plist. |
cannotFindSecret | 903 | Cannot find secret. Make sure you have 'CivicSecret' somewhere in your Info.plist. |
redirectSchemeMismatch | 904 | Cannot find a matching URL scheme for 'CivicRedirectScheme'. Please ensure 'CivicRedirectScheme' matches one of the 'CFBundleURLTypes' 'CFBundleURLSchemes'. |
cannotParseResponse | 911 | Cannot parse response from server. |
cannotParseResponseData | 912 | Cannot parse response data from server. |
invalidUrl | 913 | Invalid url. |
invalidRequest | 914 | Invalid request. |
invalidSession | 921 | Invalid session. |
mobileUpgrade | 922 | Mobile upgrade required. |
userCancelled | 923 | User cancelled scope request. |
verifyError | 924 | Error occurred during verification. |
userDataNotAvailable | 202 | User data is still not available. Try poll again later. |
scopeRequestTimeOut | 925 | Scope request timed out. |
verificationFailed | 931 | Failed to verify the response. |
decryptionFailed | 932 | Failed to decrypt response data. |
secretNotFound | 933 | Cannot find secret. Please ensure you provide the library with a valid secret. |
decodingFailed | 997 | Failed to decode the json to an object. |
authenticationUnknownError | 998 | Unknown authentication error. |
unknown | 999 | Unknown error. |
The following table shows the potential errors that can occur via the server api:
Error | Status Code | Message |
---|---|---|
badRequest | 400 | Check the response ‘message’ field for details. |
unauthorized | 401 | Authentication failed. |
methodNotAllowed | 405 | You tried to access an invalid method. |
insufficientFunds | 409 | Unauthorized: Insufficient funds. |
tooManyRequests | 429 | Your request was throttled by our gateway. |
internalServer | 500 | We had a problem with our server. Try again later. |
timeOut | 504 | Endpoint Request Timed-out Exception. |
FAQ
What do we use for the redirect scheme?
You can use any unique scheme that is valid for your app.
UIImage
from the UserInfo
?
How do we get the Some UserInfo
objects have a value encoded in base 64, an example would be for the documents.genericId.image
UserInfo
object, we would decode to an UIImage
with the following code snippet:
func decodeBase64ToImage(_ userInfo: UserInfo) -> UIImage? {
guard let data = Data(base64Encoded: userInfo.value, options: .ignoreUnknownCharacters) else {
return nil
}
return UIImage(data: data)
}
Unauthorized: no platforms found for provided app_id
?
What does it mean when I get It means you have supplied the incorrect Application Identifier. Please ensure you have the correct App ID from the Integration Portal under the configured application.
Unauthorized: mobileId not found in partner platform list
?
What does it mean when I get It means you have not supplied the correct Bundle ID or have not configured your mobile application on the Integration Portal.
How do I just retrieve the JWT token so that I can handle the user data on my side?
In order to retrieve the JWT token only, in your implementation for the ConnectDelegate
, implement the func connectShouldFetchUserData(withToken token: String) -> Bool
function and return false. The token
provided in that function is the JWT token and can be handled by whichever way needed.
Author
See also the list of contributors who participated in this project.
Contribution
Find contributing guidelines in the CONTRIBUTING file.
License
Civic App Connect is available under the MIT license. See the LICENSE file for more info.