Skip to content

Commit

Permalink
Fix #619: Support for OIDC activation
Browse files Browse the repository at this point in the history
  • Loading branch information
hvge committed Sep 18, 2024
1 parent 6197f6f commit 195cc1a
Show file tree
Hide file tree
Showing 9 changed files with 395 additions and 31 deletions.
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

0 comments on commit 195cc1a

Please sign in to comment.