Skip to content

Commit

Permalink
GH-186 adding full (documented)OAuth2 Support (#223) (#224)
Browse files Browse the repository at this point in the history
This PR introduces version `2.0.0` with a few breaking changes to reach simplified code and easier API in our sdk:
* added oAuth support in aem (GH-186)
* simplified/trimmed-down`aio-lib-java-ims` API:  
  * the `ImsService` interface is unified (wether leveraging JWT or oAuth)
  * all `systemEnv`, `properties` and `configMap` Builders methods are removed and replaced by
     * an enriched `WorkspaceUtil` allowing a safer load of the system properties. 
  * with simpler JWT support
     * trimming down the privateKey configurations options to just one: using a base64 encoded pkcs8 format
* updated the various docs and test drive accordingly (GH-172)
* updated the various unit-tests and adding a bit for junit coverage
* deleted of a few unused configurations files
* added a new `stage_oauth_itest` GH workflow and `aio_stage_oauth` environment and GH oauth e2e
  • Loading branch information
francoisledroff authored Sep 2, 2024
1 parent 51173cf commit f2f62de
Show file tree
Hide file tree
Showing 60 changed files with 897 additions and 1,267 deletions.
40 changes: 35 additions & 5 deletions .github/workflows/maven-itest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,41 @@ jobs:
aio_api_url: ${{ secrets.aio_api_url }}
aio_publish_url: ${{ secrets.aio_publish_url }}

stage_oauth_itest:
runs-on: ubuntu-latest
environment: aio_stage_oauth
strategy:
fail-fast: true

steps:

# Check out Git repository
- name: Checkout code
uses: actions/checkout@v3

# Set up environment with Java and Maven
- name: Setup JDK
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: 11
cache: maven

# Build & Test
- name: Stage Integration Test with Maven
run: mvn -Daio_publish_url=$aio_publish_url -Daio_api_url=$aio_api_url -Daio_ims_url=$aio_ims_url -Daio_oauth_scopes=$aio_oauth_scopes -Daio_ims_org_id=$aio_ims_org_id -Daio_consumer_org_id=$aio_consumer_org_id -Daio_project_id=$aio_project_id -Daio_workspace_id=$aio_workspace_id -Daio_api_key=$aio_api_key -Daio_client_secret=$aio_client_secret verify -Pitest
env:
aio_ims_url: ${{ secrets.aio_ims_url }}
aio_client_secret: ${{ secrets.aio_client_secret }}
aio_oauth_scopes: ${{ secrets.aio_oauth_scopes }}
aio_ims_org_id: ${{ secrets.aio_ims_org_id }}
aio_consumer_org_id: ${{ secrets.aio_consumer_org_id }}
aio_project_id: ${{ secrets.aio_project_id }}
aio_workspace_id: ${{ secrets.aio_workspace_id }}
aio_api_key: ${{ secrets.aio_api_key }}
aio_api_url: ${{ secrets.aio_api_url }}
aio_publish_url: ${{ secrets.aio_publish_url }}

prod_itest:
runs-on: ubuntu-latest
environment: aio_prod
Expand Down Expand Up @@ -93,8 +128,3 @@ jobs:
aio_encoded_pkcs8: ${{ secrets.aio_encoded_pkcs8 }}
aio_api_url: ${{ secrets.aio_api_url }}
aio_publish_url: ${{ secrets.aio_publish_url }}





2 changes: 1 addition & 1 deletion aem/aio_aem_events/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<groupId>com.adobe.aio.aem</groupId>
<artifactId>aio-aem</artifactId>
<relativePath>../pom.xml</relativePath>
<version>1.1.29-SNAPSHOT</version>
<version>2.0.0-SNAPSHOT</version>
</parent>

<artifactId>aio-aem-events</artifactId>
Expand Down
48 changes: 15 additions & 33 deletions aem/core_aem/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ wrapping [`aio-lib-java-core`](../../core) and [`aio-lib-java-ims`](../../ims)

It hosts the services to
* get the Adobe Developer Console Workspace
* get Access token (from JWT exchange token flow) from Adobe Identity Management System (IMS)
* get Access token (using either JWT or OAuth token flow) from Adobe Identity Management System (IMS)


## `Workspace` Configuration
Expand All @@ -18,28 +18,22 @@ service looks up the following OSGI configuration keys:
* `aio.consumer.org.id` your Adobe Developer Console consumer orgnaization id (`project.org.id`)
* `aio.ims.org.id` your Adobe Developer Console IMS Organization ID (`project.org.ims_org_id`)
* `aio.workspace.id` your Adobe Developer Console workspace Id (`project.workspace.id`)
* `aio.credential.id` your Adobe Developer Console jwt credential id (`project.workspace.details.credentials[i].id`)
* `aio.api.key` your Adobe Developer Console jwt credential API Key (or Client ID) (`project.workspace.details.credentials[i].jwt.client_id`)
* `aio.client.secret` your Adobe Developer Console jwt credential client secret (`project.workspace.details.credentials[i].jwt.client_secret`)
* `aio.meta.scopes` a comma separated list of metascopes associated with your API, see your Adobe Developer Console jwt credential metascopes (`project.workspace.details.credentials[i].jwt.meta_scopes`)
* `aio.technical.account.id` your Adobe Developer Console jwt credential technical account id (`project.workspace.details.credentials[i].jwt.technical_account_id`)
* `aio.encoded.pkcs8` your private key (in a base64 encoded pkcs8 format) see below
* `aio.api.key` your Adobe Developer Console credential API Key (or Client ID)

When using JWT credentials also set
* `aio.credential.id` your Adobe Developer Console jwt credential id
* `aio.client.secret` your Adobe Developer Console jwt credential client secret
* `aio.meta.scopes` a comma separated list of metascopes associated with your API, see your Adobe Developer Console jwt credential metascopes
* `aio.technical.account.id` your Adobe Developer Console jwt credential technical account id
* `aio.encoded.pkcs8` your private key (in a base64 encoded pkcs8 format)

### `aio.encoded.pkcs8`
When using OAuth credentials also set
* `aio.client.secret` your Adobe Developer Console oAuth credential client secret
* `aio_oauth_scopes` a comma separated list of OAuth scopes associated with your API, see your Adobe Developer Console OAuth scopes (project.workspace.details.credentials[i].oauth_server_to_server.scopes)

`aio.encoded.pkcs8` configuration value is associated with your Adobe Developer Console private key.
It is a string: your private key in a pkcs8 format, base64 encoded, here is how to generate it:

First, convert your private key to a PKCS8 format, use the following command:

openssl pkcs8 -topk8 -inform PEM -outform DER -in private.key -nocrypt > private.pkcs8.key

Then, base 64 encode it, use the following command:

base64 private.pkcs8.key

For more details check our [`aio-lib-java-ims` documentation](../../ims/README.md)
For more details on the above please refer to
* [`aio-lib-java-core` docs](../../core/README.md) for more details
* [`aio-lib-java-ims` docs](../../ims/README.md) for more details

### `on premise` AEM configuration:
When running AEM on premise:
Expand All @@ -66,19 +60,7 @@ The response json payload should like this:
"workspace": {
"imsUrl": "https://ims-na1.adobelogin.com",
"imsOrgId": "...@AdobeOrg",
"apiKey": "...",
"credentialId": "...",
"technicalAccountId": "...@techacct.adobe.com",
"metascopes": [
"...",
"/s/ent_adobeio_sdk"
],
"consumerOrgId": "...",
"projectId": "...",
"workspaceId": "...",
"projectUrl": "https://developer.adobe.com/console/projects/.../.../overview",
"clientSecretDefined": true,
"privateKeyDefined": true
"apiKey": "..."
}
},
"error": null
Expand Down
2 changes: 1 addition & 1 deletion aem/core_aem/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<groupId>com.adobe.aio.aem</groupId>
<artifactId>aio-aem</artifactId>
<relativePath>../pom.xml</relativePath>
<version>1.1.29-SNAPSHOT</version>
<version>2.0.0-SNAPSHOT</version>
</parent>

<artifactId>aio-aem-core</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@
import com.adobe.aio.aem.status.Status;
import com.adobe.aio.aem.workspace.WorkspaceSupplier;
import com.adobe.aio.aem.workspace.ocd.WorkspaceConfig;
import com.adobe.aio.auth.Context;
import com.adobe.aio.auth.JwtContext;
import com.adobe.aio.auth.OAuthContext;
import com.adobe.aio.ims.util.PrivateKeyBuilder;
import com.adobe.aio.util.WorkspaceUtil;
import com.adobe.aio.workspace.Workspace;
import java.security.PrivateKey;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
Expand Down Expand Up @@ -48,8 +51,6 @@ protected void activate(final WorkspaceConfig config) {
public Status getStatus() {
Map<String, Object> details = new HashMap<>();
try {
details.put("workspace", Workspace.builder()
.configMap(getAuthConfigMap(workspaceConfig)).build());
Workspace workspace = getWorkspace();
details.put("workspace", workspace);
workspace.validateAll();
Expand All @@ -67,33 +68,35 @@ public Status getStatus() {
*/
@Override
public Workspace getWorkspace() {
if (!StringUtils.isEmpty(workspaceConfig.aio_encoded_pkcs8())) {
PrivateKey privateKey = new PrivateKeyBuilder()
.encodedPkcs8Key(workspaceConfig.aio_encoded_pkcs8()).build();
return Workspace.builder()
.configMap(getAuthConfigMap(workspaceConfig))
.privateKey(privateKey).build();
} else {
return Workspace.builder()
.configMap(getAuthConfigMap(workspaceConfig)).build();
}
return WorkspaceUtil.getWorkspaceBuilder(getAuthConfigMap(workspaceConfig)).build();
}

private Map<String, String> getAuthConfigMap(
WorkspaceConfig config) {
Map<String, String> map = new HashMap<String, String>();
map.put(Workspace.API_KEY, config.aio_api_key());
map.put(Workspace.CLIENT_SECRET, config.aio_client_secret());
map.put(Workspace.CONSUMER_ORG_ID, config.aio_consumer_org_id());
map.put(Workspace.CREDENTIAL_ID, config.aio_credential_id());
map.put(Workspace.IMS_ORG_ID, config.aio_ims_org_id());
map.put(Workspace.IMS_URL, config.aio_ims_url());
map.put(Workspace.PROJECT_ID, config.aio_project_id());
map.put(Workspace.TECHNICAL_ACCOUNT_ID, config.aio_technical_account_id());
map.put(Workspace.WORKSPACE_ID, config.aio_workspace_id());
map.put(Workspace.META_SCOPES, config.aio_meta_scopes());
putIfNotBlank(map, Workspace.API_KEY, config.aio_api_key());
putIfNotBlank(map, Workspace.CONSUMER_ORG_ID, config.aio_consumer_org_id());
putIfNotBlank(map, Workspace.IMS_ORG_ID, config.aio_ims_org_id());
putIfNotBlank(map, Workspace.IMS_URL, config.aio_ims_url());
putIfNotBlank(map, Workspace.PROJECT_ID, config.aio_project_id());
putIfNotBlank(map, Workspace.WORKSPACE_ID, config.aio_workspace_id());
putIfNotBlank(map, Workspace.CREDENTIAL_ID, config.aio_credential_id());

putIfNotBlank(map, Context.CLIENT_SECRET, config.aio_client_secret());


putIfNotBlank(map, JwtContext.TECHNICAL_ACCOUNT_ID, config.aio_technical_account_id());
putIfNotBlank(map, JwtContext.META_SCOPES, config.aio_meta_scopes());
putIfNotBlank(map, PrivateKeyBuilder.AIO_ENCODED_PKCS_8, config.aio_encoded_pkcs8());

putIfNotBlank(map, OAuthContext.SCOPES, config.aio_oauth_scopes());
return map;
}

private void putIfNotBlank(Map<String, String> map, String key, String value) {
if (StringUtils.isNotBlank(value)) {
map.put(key, value);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,14 @@
description = "Adobe IMS URL: prod: https://ims-na1.adobelogin.com | stage: https://ims-na1-stg1.adobelogin.com")
String aio_ims_url() default "https://ims-na1.adobelogin.com";

@AttributeDefinition(name = "Meta Scopes",
description = "Comma separated list of metascopes associated with your API (`/s/event_receiver_api,/s/ent_adobeio_sdk` for instance) (project.workspace.details.credentials.jwt.meta_scopes)")
@AttributeDefinition(name = "JWT Meta Scopes (For deprecated JWT Auth only)",
description = "Comma separated list of metascopes associated with your API (`/s/event_receiver_api,/s/ent_adobeio_sdk` for instance) (project.workspace.details.credentials.jwt.meta_scopes), to be used for deprecated JWT Auth only.")
String aio_meta_scopes() default "/s/ent_adobeio_sdk";

@AttributeDefinition(name = "OAuth Scopes",
description = "Comma separated String. list of oauth scopes associated with your API (project.workspace.details.credentials.oauth_server_to_server.scopes)")
String aio_oauth_scopes();

@AttributeDefinition(name = "IMS ORG ID",
description = "Adobe IMS Organization ID as shown in your Adobe Developer Console workspace (project.org.ims_org_id)")
String aio_ims_org_id();
Expand All @@ -43,23 +47,23 @@
String aio_workspace_id();

@AttributeDefinition(name = "API Key (Client ID)",
description = "Adobe I/O API Key (Client ID) as shown in in your Adobe Developer Console workspace (project.workspace.details.credentials.jwt.client_id)")
description = "Adobe I/O API Key (Client ID) as shown in in your Adobe Developer Console workspace")
String aio_api_key();

@AttributeDefinition(name = "Credential ID",
description = "Adobe I/O Credential ID as shown in your Adobe Developer Console workspace (project.workspace.details.credentials.id)")
String aio_credential_id();

@AttributeDefinition(name = "Technical Account ID",
@AttributeDefinition(name = "Technical Account ID (For deprecated JWT Auth only)",
description = "Technical account ID as shown in your Adobe Developer Console workspace (project.workspace.details.credentials.jwt.technical_account_id)")
String aio_technical_account_id();

@AttributeDefinition(name = "Client Secret",
description = "Adobe I/O Client Secret as shown in your Adobe Developer Console workspace (project.workspace.details.credentials.jwt.client_secret)")
description = "Adobe I/O Client Secret as shown in your Adobe Developer Console workspace")
String aio_client_secret();

@AttributeDefinition(name = "Private Key",
description = "Base64 encoded pkcs8 Private Key.")
@AttributeDefinition(name = "Private Key (For deprecated JWT Auth only)",
description = "Base64 encoded pkcs8 Private Key (For deprecated JWT Auth only).")
String aio_encoded_pkcs8();

}
2 changes: 1 addition & 1 deletion aem/events_ingress_aem/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<groupId>com.adobe.aio.aem</groupId>
<artifactId>aio-aem</artifactId>
<relativePath>../pom.xml</relativePath>
<version>1.1.29-SNAPSHOT</version>
<version>2.0.0-SNAPSHOT</version>
</parent>

<artifactId>aio-aem-events-publish</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion aem/events_mgmt_aem/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<groupId>com.adobe.aio.aem</groupId>
<artifactId>aio-aem</artifactId>
<relativePath>../pom.xml</relativePath>
<version>1.1.29-SNAPSHOT</version>
<version>2.0.0-SNAPSHOT</version>
</parent>

<artifactId>aio-aem-events-mgmt</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion aem/events_osgi_mapping/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<groupId>com.adobe.aio.aem</groupId>
<artifactId>aio-aem</artifactId>
<relativePath>../pom.xml</relativePath>
<version>1.1.29-SNAPSHOT</version>
<version>2.0.0-SNAPSHOT</version>
</parent>

<artifactId>aio-aem-events-osgi-mapping</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion aem/lib_osgi/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<groupId>com.adobe.aio.aem</groupId>
<artifactId>aio-aem</artifactId>
<relativePath>../pom.xml</relativePath>
<version>1.1.29-SNAPSHOT</version>
<version>2.0.0-SNAPSHOT</version>
</parent>

<artifactId>aio-lib-osgi</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion aem/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<groupId>com.adobe.aio</groupId>
<artifactId>aio-lib-java</artifactId>
<relativePath>../pom.xml</relativePath>
<version>1.1.29-SNAPSHOT</version>
<version>2.0.0-SNAPSHOT</version>
</parent>

<groupId>com.adobe.aio.aem</groupId>
Expand Down
17 changes: 7 additions & 10 deletions core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ This library holds a [`Workspace`](./src/main/java/com/adobe/aio/workspace/Works
your [Adobe Developer Console Project Workspace](https://www.adobe.io/apis/experienceplatform/console/docs.html#!AdobeDocs/adobeio-console/master/projects.md),

To get you started quickly use a `.properties` file,
* see our [sample config file](./src/test/resources/workspace.properties) in our jUnit Test.
* see our sample config files:
* [workspace.jwt.properties](./src/test/resources/workspace.jwt.properties)
* [workspace.oauth.properties](./src/test/resources/workspace.oauth.properties)
* download your `project` configurations file from your Adobe Developer Console Project overview page
* map your `project` configurations with this properties

Expand All @@ -23,9 +25,11 @@ The `Workspace` POJO holds your Adobe Developer Console Project configurations
* `aio_consumer_org_id` your Adobe Developer Console consumer orgnaization id (`project.org.id`)
* `aio_ims_org_id` your Adobe Developer Console IMS Organization ID (`project.org.ims_org_id`)
* `aio_workspace_id` your Adobe Developer Console workspace Id (`project.workspace.id`)
* `aio_credential_id` your Adobe Developer Console credential id (`project.workspace.details.credentials[i].id`)
* this is optional, but it might be handy to have it in your `Workspace` POJO, to avoid confusion when you have multiple credentials, and to eventually in some Adobe API calls

### Workspace Authentication Context
The `Workspace` POJO must also hold your Adobe Developer Auth configurations, pick one of the following authentication methods:
The `Workspace` POJO must also hold your Adobe Developer Auth configurations, pick one of the following authentication methods (see [aio-lib-java-ims](../ims/README.md) docs for more details):

#### OAuth2 authentication
For [OAuth2 authentication](https://developer.adobe.com/developer-console/docs/guides/authentication/ServerToServerAuthentication/#oauth-server-to-server-credential), you will need to provide the following properties:
Expand All @@ -35,20 +39,13 @@ For [OAuth2 authentication](https://developer.adobe.com/developer-console/docs/g

#### JWT authentication
For [JWT authentication](https://developer.adobe.com/developer-console/docs/guides/authentication/ServerToServerAuthentication/#service-account-jwt-credential-deprecated), you will need to provide the following properties:
* `aio_credential_id` your Adobe Developer Console jwt credential id (`project.workspace.details.credentials[i].id`)
* `aio_client_secret` your Adobe Developer Console jwt credential client secret (`project.workspace.details.credentials[i].jwt.client_secret`)
* `aio_api_key` your Adobe Developer Console jwt credential API Key (or Client ID) (`project.workspace.details.credentials[i].jwt.client_id`)
* `aio_meta_scopes` a comma separated list of metascopes associated with your API, see your Adobe Developer Console jwt credential metascopes (`project.workspace.details.credentials[i].jwt.meta_scopes`)
* `aio_technical_account_id` your Adobe Developer Console jwt credential technical account id (`project.workspace.details.credentials[i].jwt.technical_account_id`)
* `aio_encoded_pkcs8` your privateKey (associated with the public key set in your Adobe Developer Console workspace) in a base64 encoded pkcs8 format


On top of these, the [`Workspace`](./src/main/java/com/adobe/aio/workspace/Workspace.java) POJO can also hold your JWT private Key
(associated with the public key you uploaded in your Adobe Developer Console Workspace)
this will help to power Adobe JWT authentication flow and transparently add the proper `Bearer`
authentication token to all your request,
Note the easiest way is to stuff your Private Key as a `pkcs8` base64 encoded String
using the `aio_encoded_pkcs8` property key.
confer [aio-lib-java-ims](../ims/README.md#Create-and-configure-your-public-and-private-key) documentation for more details.

## Builds

Expand Down
2 changes: 1 addition & 1 deletion core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<parent>
<groupId>com.adobe.aio</groupId>
<artifactId>aio-lib-java</artifactId>
<version>1.1.29-SNAPSHOT</version>
<version>2.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
Expand Down
9 changes: 3 additions & 6 deletions core/src/main/java/com/adobe/aio/auth/Context.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,10 @@
public interface Context {

/**
* Property name used in maps and config files for setting the AIO IMS URL.
* Property name for looking up Authentication Client Secret in various contexts.
* Reference: <a href="https://developer.adobe.com/developer-console/docs/guides/authentication/ServerToServerAuthentication/#server-to-server-credential-types">AIO Developer Documentation</a>
*/
public static final String IMS_URL = "aio_ims_url";
/**
* Property name used in maps and config files for setting the AIO IMS Org Id.
*/
public static final String IMS_ORG_ID = "aio_ims_org_id";
String CLIENT_SECRET = "aio_client_secret";

/**
* Validates this context is minimally populated and able to function.
Expand Down
Loading

0 comments on commit f2f62de

Please sign in to comment.