Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for OIDC activation #622

Merged
merged 1 commit into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/Migration-from-1.8-to-1.9.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ PowerAuth Mobile SDK in version `1.9.0` provides the following improvements:
- The PowerAuth protocol is no longer use EC key-pairs for encryption and signature calculation (dual use problem.)
- The End-To-End encryption is now using a temporary keys to improve the forward secrecy of our ECIES scheme.
- Simplified construction of encrypted request and response. Check updated [Android](PowerAuth-SDK-for-Android.md#end-to-end-encryption) or [iOS](PowerAuth-SDK-for-iOS.md#end-to-end-encryption) documentation for more details.
- Now it's possible to create an activation via OpenID Connect provider.

### Compatibility with PowerAuth Server

Expand Down
40 changes: 40 additions & 0 deletions docs/PowerAuth-SDK-for-Android.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- [SDK Configuration](#configuration)
- [Device Activation](#activation)
- [Activation via Activation Code](#activation-via-activation-code)
- [Activation via OpenID Connect](#activation-via-openid-connect)
- [Activation via Custom Credentials](#activation-via-custom-credentials)
- [Activation via Recovery Code](#activation-via-recovery-code)
- [Customize Activation](#customize-activation)
Expand Down Expand Up @@ -347,6 +348,45 @@ try {
Be aware that OTP can be used only if the activation is configured for ON_KEY_EXCHANGE validation on the PowerAuth server. See our [crypto documentation for details](https://github.com/wultra/powerauth-crypto/blob/develop/docs/Additional-Activation-OTP.md#regular-activation-with-otp).
<!-- end -->

### Activation via OpenID Connect

You may also create an activation using OIDC protocol:

```kotlin
// Create a new activation with a given device name and login credentials
val deviceName = "Juraj's JiaYu S3"
// Get the following information from your OpenID provider
val providerId = "1234567890abcdef"
val code = "1234567890abcdef"
val nonce = "K1mP3rT9bQ8lV6zN7sW2xY4dJ5oU0fA1gH29o"
val codeVerifier = "G3hsI1KZX1o~K0p-5lT3F7yZ4...6yP8rE2wO9n" // code verifier is optional

// Create an activation object with the given credentials.
val activation: PowerAuthActivation
try {
activation = PowerAuthActivation.Builder.oidcActivation(providerId, code, nonce, codeVerifier)
.setActivationName(deviceName)
.build()
} catch (e: PowerAuthErrorException) {
// Credentials dictionary is empty
}

// Create a new activation with the given activation object
powerAuthSDK.createActivation(activation, object: ICreateActivationListener {
override fun onActivationCreateSucceed(result: CreateActivationResult) {
val fingerprint = result.activationFingerprint
val activationRecovery = result.recoveryData
// No error occurred, proceed to credentials entry (PIN prompt, Enable "Biometric Authentication" switch, ...) and persist
// The 'fingerprint' value represents the combination of device and server public keys - it may be used as visual confirmation
// If the server supports recovery codes for activation, then `activationRecovery` contains object with information about activation recovery.
}

override fun onActivationCreateFailed(t: Throwable) {
// Error occurred, report it to the user
}
})
```

### Activation via Custom Credentials

You may also create an activation using any custom login data - it can be anything that the server can use to obtain the user ID to associate with a new activation. Since the credentials are custom, the server's implementation must be able to process such a request. Unlike the previous versions of SDK, the custom activation no longer requires a custom activation endpoint.
Expand Down
25 changes: 25 additions & 0 deletions docs/PowerAuth-SDK-for-iOS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
- [SDK Configuration](#configuration)
- [Device Activation](#activation)
- [Activation via Activation Code](#activation-via-activation-code)
- [Activation via OpenID Connect](#activation-via-openid-connect)
- [Activation via Custom Credentials](#activation-via-custom-credentials)
- [Activation via Recovery Code](#activation-via-recovery-code)
- [Customize Activation](#customize-activation)
Expand Down Expand Up @@ -234,6 +235,30 @@ guard let activation = try? PowerAuthActivation(activationCode: activationCode,
Be aware that OTP can be used only if the activation is configured for ON_KEY_EXCHANGE validation on the PowerAuth server. See our [crypto documentation for details](https://github.com/wultra/powerauth-crypto/blob/develop/docs/Additional-Activation-OTP.md#regular-activation-with-otp).
<!-- end -->

### Activation via OpenID Connect

You may also create an activation using OIDC protocol:

```swift
// Create a new activation with a given device name and custom login credentials
let deviceName = "Petr's iPhone 7" // or UIDevice.current.name (see warning below)
// Get the following information from your OpenID provider
let providerId = "1234567890abcdef"
let code = "1234567890abcdef"
let nonce = "K1mP3rT9bQ8lV6zN7sW2xY4dJ5oU0fA1gH29o"
let codeVerifier = "G3hsI1KZX1o~K0p-5lT3F7yZ4...6yP8rE2wO9n" // code verifier is optional

// create an activation object with the given OIDC parameters
guard let activation = try? PowerAuthActivation(oidcProviderId: providerId, code: code, nonce: nonce, codeVerifier: codeVerifier)
.with(activationName: deviceName) else {
// Activation parameter contains empty string
}
```

<!-- begin box warning -->
Note that if you use `UIDevice.current.name` for a device’s name, your application must include an [appropriate entitlement](https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_device-information_user-assigned-device-name); otherwise, the operating system will provide a generic `iPhone` string.
<!-- end -->

### Activation via Custom Credentials

You may also create an activation using any custom login data - it can be anything that the server can use to obtain the user ID to associate with a new activation. Since the credentials are custom, the server's implementation must be able to process such a request. Unlike the previous versions of SDK, the custom activation no longer requires a custom activation endpoint.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ public void createStandardActivationWithExtras() throws Exception {
additionalAttributes.put("test", "value");
additionalAttributes.put("zero", 0);

PowerAuthActivation activation = PowerAuthActivation.Builder.activation("W65WE-3T7VI-7FBS2-A4OYA", "Named activation")
PowerAuthActivation activation = PowerAuthActivation.Builder.activation("W65WE-3T7VI-7FBS2-A4OYA")
.setActivationName("Named activation")
.setExtras("extras")
.setCustomAttributes(additionalAttributes)
.build();
Expand Down Expand Up @@ -123,15 +124,85 @@ public void createStandardActivationWithOtp() throws Exception {

@Test(expected = PowerAuthErrorException.class)
public void createStandardActivationWithInvalidCode() throws Exception {
PowerAuthActivation.Builder.activation("W65WE-3T7VI-7FBS2-A4OYB", null)
PowerAuthActivation.Builder.activation("W65WE-3T7VI-7FBS2-A4OYB")
.build();
}

// OIDC

@Test
public void createOidcActivation() throws Exception {
final String providerId = "abc123";
final String code = "ABCDEFGH";
final String nonce = "K1mP3rT9bQ8lV6zN7sW2xY4dJ5oU0fA1gH29o";
final String codeVerifier = "G3hsI1KZX1o~K0p-5lT3F7yZ4bC8dE2jX9aQ6nO2rP3uS7wT5mV8jW1oY6xB3sD09tR4vU3qM1nG7kL6hV5wY2pJ0aF3eK9dQ8xN4mS2zB7oU5tL1cJ3vX6yP8rE2wO9n";
PowerAuthActivation activation = PowerAuthActivation.Builder.oidcActivation(providerId, code, nonce, null)
.build();

assertNotNull(activation);
assertEquals(ActivationType.DIRECT, activation.activationType);
assertNotNull(activation.identityAttributes);
assertEquals("oidc", activation.identityAttributes.get("method"));
assertEquals(providerId, activation.identityAttributes.get("providerId"));
assertEquals(code, activation.identityAttributes.get("code"));
assertEquals(nonce, activation.identityAttributes.get("nonce"));
assertNull(activation.identityAttributes.get("codeVerifier"));
assertNull(activation.activationName);

activation = PowerAuthActivation.Builder.oidcActivation(providerId, code, nonce, codeVerifier)
.build();

assertNotNull(activation);
assertEquals(ActivationType.DIRECT, activation.activationType);
assertNotNull(activation.identityAttributes);
assertEquals("oidc", activation.identityAttributes.get("method"));
assertEquals(providerId, activation.identityAttributes.get("providerId"));
assertEquals(code, activation.identityAttributes.get("code"));
assertEquals(nonce, activation.identityAttributes.get("nonce"));
assertEquals(codeVerifier, activation.identityAttributes.get("codeVerifier"));
assertNull(activation.activationName);
}

@Test
public void createOidcActivationWithName() throws Exception {
final String providerId = "abc123";
final String code = "ABCDEFGH";
final String nonce = "K1mP3rT9bQ8lV6zN7sW2xY4dJ5oU0fA1gH29o";
final String codeVerifier = "G3hsI1KZX1o~K0p-5lT3F7yZ4bC8dE2jX9aQ6nO2rP3uS7wT5mV8jW1oY6xB3sD09tR4vU3qM1nG7kL6hV5wY2pJ0aF3eK9dQ8xN4mS2zB7oU5tL1cJ3vX6yP8rE2wO9n";
PowerAuthActivation activation = PowerAuthActivation.Builder.oidcActivation(providerId, code, nonce, null)
.setActivationName("OIDC Activation")
.build();

assertNotNull(activation);
assertEquals(ActivationType.DIRECT, activation.activationType);
assertNotNull(activation.identityAttributes);
assertEquals("oidc", activation.identityAttributes.get("method"));
assertEquals(providerId, activation.identityAttributes.get("providerId"));
assertEquals(code, activation.identityAttributes.get("code"));
assertEquals(nonce, activation.identityAttributes.get("nonce"));
assertNull(activation.identityAttributes.get("codeVerifier"));
assertEquals("OIDC Activation", activation.activationName);

activation = PowerAuthActivation.Builder.oidcActivation(providerId, code, nonce, codeVerifier)
.setActivationName("OIDC Activation")
.build();

assertNotNull(activation);
assertEquals(ActivationType.DIRECT, activation.activationType);
assertNotNull(activation.identityAttributes);
assertEquals("oidc", activation.identityAttributes.get("method"));
assertEquals(providerId, activation.identityAttributes.get("providerId"));
assertEquals(code, activation.identityAttributes.get("code"));
assertEquals(nonce, activation.identityAttributes.get("nonce"));
assertEquals(codeVerifier, activation.identityAttributes.get("codeVerifier"));
assertEquals("OIDC Activation", activation.activationName);
}

// Recovery activations

@Test
public void createRecoveryActivation() throws Exception {
PowerAuthActivation activation = PowerAuthActivation.Builder.recoveryActivation("W65WE-3T7VI-7FBS2-A4OYA", "1234567890", null)
PowerAuthActivation activation = PowerAuthActivation.Builder.recoveryActivation("W65WE-3T7VI-7FBS2-A4OYA", "1234567890")
.build();

assertNotNull(activation);
Expand All @@ -144,7 +215,7 @@ public void createRecoveryActivation() throws Exception {

@Test
public void createRecoveryActivationFromQrCode() throws Exception {
PowerAuthActivation activation = PowerAuthActivation.Builder.recoveryActivation("R:W65WE-3T7VI-7FBS2-A4OYA", "1234567890", null)
PowerAuthActivation activation = PowerAuthActivation.Builder.recoveryActivation("R:W65WE-3T7VI-7FBS2-A4OYA", "1234567890")
.build();

assertNotNull(activation);
Expand All @@ -157,7 +228,8 @@ public void createRecoveryActivationFromQrCode() throws Exception {

@Test
public void createRecoveryActivationWithName() throws Exception {
PowerAuthActivation activation = PowerAuthActivation.Builder.recoveryActivation("W65WE-3T7VI-7FBS2-A4OYA", "1234567890", "Recovery")
PowerAuthActivation activation = PowerAuthActivation.Builder.recoveryActivation("W65WE-3T7VI-7FBS2-A4OYA", "1234567890")
.setActivationName("Recovery")
.build();

assertNotNull(activation);
Expand Down Expand Up @@ -193,20 +265,21 @@ public void createRecoveryActivationWithExtras() throws Exception {

@Test(expected = PowerAuthErrorException.class)
public void createRecoveryActivationWithOtp() throws Exception {
PowerAuthActivation.Builder.recoveryActivation("W65WE-3T7VI-7FBS2-A4OYA", "1234567890", "Recovery")
PowerAuthActivation.Builder.recoveryActivation("W65WE-3T7VI-7FBS2-A4OYA", "1234567890")
.setActivationName("Recovery")
.setAdditionalActivationOtp("1234")
.build();
}

@Test(expected = PowerAuthErrorException.class)
public void createRecoveryActivationWithInvalidCode() throws Exception {
PowerAuthActivation.Builder.recoveryActivation("W65WE-3T7VI-7FBS2-A4OYB", "1234567890", null)
PowerAuthActivation.Builder.recoveryActivation("W65WE-3T7VI-7FBS2-A4OYB", "1234567890")
.build();
}

@Test(expected = PowerAuthErrorException.class)
public void createRecoveryActivationWithInvalidPuk() throws Exception {
PowerAuthActivation.Builder.recoveryActivation("W65WE-3T7VI-7FBS2-A4OYA", "123456789", null)
PowerAuthActivation.Builder.recoveryActivation("W65WE-3T7VI-7FBS2-A4OYA", "123456789")
.build();
}

Expand All @@ -218,7 +291,7 @@ public void createCustomActivation() throws Exception {
identityAttributes.put("login", "juraj");
identityAttributes.put("password", "nbusr123");

PowerAuthActivation activation = PowerAuthActivation.Builder.customActivation(identityAttributes, null)
PowerAuthActivation activation = PowerAuthActivation.Builder.customActivation(identityAttributes)
.build();

assertNotNull(activation);
Expand All @@ -236,7 +309,8 @@ public void createCustomActivationWithName() throws Exception {
identityAttributes.put("login", "juraj");
identityAttributes.put("password", "nbusr123");

PowerAuthActivation activation = PowerAuthActivation.Builder.customActivation(identityAttributes, "CustomActivation")
PowerAuthActivation activation = PowerAuthActivation.Builder.customActivation(identityAttributes)
.setActivationName("CustomActivation")
.build();

assertNotNull(activation);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
*/
public enum ActivationType {
CODE,
DIRECT,
CUSTOM,
RECOVERY
}
Loading
Loading