Skip to content

Commit

Permalink
fix issue with storing refreshed tokens, improve admin page and docum…
Browse files Browse the repository at this point in the history
…entation
  • Loading branch information
stefanmasek committed Sep 15, 2023
1 parent 1b434bf commit b2a70e6
Show file tree
Hide file tree
Showing 10 changed files with 81 additions and 48 deletions.
31 changes: 16 additions & 15 deletions adobe-esign-connector-product/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,23 @@ YOUR DEMO DESCRIPTION GOES HERE

## Setup

To use the Adobe Sign Connector it needs to be connected with Adobe. An Adobe administration account needs to be created.
To use the Adobe Sign Connector it needs to be connected with Adobe. An Adobe administration account needs to be created.

### Creating Adobe Sign account (Company/Developer)

1. Create a **AdobeSign** Company **Account** OR for Developer Account creation use [Create Developer Account, APIs for custom applications | Acrobat Sign](https://www.adobe.com/sign/developer-form.html) an follow the steps:
a. Fill form with personal and business data + continue
![fill-account-info](images/createAccountFillInfo.png)

b. Provide a password + continue
![fill-password](images/createAccountPassword.png)

c. Provide date of birth + continue
![fill-birth-date](images/createAccountBirthDate.png)

d. You will receive a verification code. Insert the code. Will automatically continue
![verification-code](images/createAccountVerificationCode.png)

e. Developer account is created
![account-finished](images/createAccountFinished.png)

Expand All @@ -55,9 +55,9 @@ To be able to open the Admin Setup page the admin user needs to own `ADOBE_ESIGN
##### How to get Integration Key

1. Go to your Adobe Sign account page: https://secure.adobesign.com/account/
2. Open **Access Tokens** configuration
2. Open **Access Tokens** configuration
![access-tokens](images/integrationKey1.png)
3. Create new Integration Key
3. Create new Integration Key
![create-integration-key](images/integrationKey2.png)
4. Copy the Integration Key to the Admin Setup Page
![copy-integration-key](images/integrationKey3.png)
Expand All @@ -67,13 +67,13 @@ Adobe API doc references for OAuth

1. https://secure.adobesign.com/public/static/oauthDoc.jsp
2. https://opensource.adobe.com/acrobat-sign/developer_guide/oauth.html

#### OAuth API Application setup
An API Application needs to be setup at Adobe Sign admin account before OAuth can be configured in the connector.
1. Go to your Adobe Sign account page: https://secure.adobesign.com/account/
2. Go to **API Applications** configuration
2. Go to **API Applications** configuration
![api-applications](images/oauth1.png)
3. Create new API Application. Set the Name, Display Name and Domain
3. Create new API Application. Set the Name, Display Name and Domain
![new-application](images/oauth2.png)
4. Open the newly created Application and copy ID and Secret to the connector's Admin Setup page
a. Application ID = `adobe-sign-connector.clientId`
Expand All @@ -90,24 +90,25 @@ An API Application needs to be setup at Adobe Sign admin account before OAuth ca
|--|--|--|
| adobe-sign-connector.baseUri | Base URI for getting the access and refresh access tokens (without the `/token` or `/refresh` part) | `https://api.eu2.adobesign.com/oauth/v2`
| adobe-sign-connector.authenticationUri| URL for the Authorization request (:exclamation:differs from tokens URL)| `https://secure.eu2.adobesign.com/public/oauth/v2`
| adobe-sign-connector.clientId| Adobe API Application Client ID|
| adobe-sign-connector.clientSecret| Adobe API Application Client Secret |
| adobe-sign-connector.clientId| Adobe API Application Client ID|
| adobe-sign-connector.clientSecret| Adobe API Application Client Secret |
| adobe-sign-connector.permissions | List of permissions that will be requested for the OAuth token | `user_read:account user_write:account user_login:account agreement_read:account agreement_write:account agreement_send:account widget_read:account widget_write:account library_read:account library_write:account workflow_read:account workflow_write:account`
| adobe-sign-connector.oauthToken | Just info about the OAuth token. Empty means there is no token initialized. To request a new token use the `Save and Request Token` button |
| adobe-sign-connector.oauthToken | Info about the OAuth refresh token. Empty means there is no token initialized. To request a new token use the `Save and Request new Token` button |
| adobe-sign-connector.accessToken| Info about the OAuth access token. |
| Redirect URI | This URI just needs to be setup to the API Application at Adobe Sign account page. (see **OAuth API Application setup** section)| `https://localhost:8444/designer/pro/adobe-esign-connector/18A83631DA63DA93/oauthResume.ivp`


#### Requesting OAuth token
:exclamation::exclamation::exclamation: Please configure all the Variables in OAuth section on the Admin Setup page (see previous section) as they are necessary for requesting the token.

1. Click the `Save and Request Token` button. You will be redirected to Adobe Sign login page if the configuration of the Variables is correct.
1. Click the `Save and Request new Token` button. You will be redirected to Adobe Sign login page if the configuration of the Variables is correct.
![save-and-request-token](images/tokenRequest1.png)
2. Login with your Adobe Sign account
![adobe-login](images/tokenRequest2.png)
3. After successful login you should see all the requested permissions. Click **Allow Access**.
3. After successful login you should see all the requested permissions. Click **Allow Access**.
![request-permissions](images/tokenRequest3.png)
4. The token will be retrieved and you should be redirected back to the connector's Admin Setup page and should be able to see the initialized token.
![token](images/tokenRequest4.png)
```
@variables.yaml@
```
```
Binary file modified adobe-esign-connector-product/images/adminPage.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified adobe-esign-connector-product/images/tokenRequest1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified adobe-esign-connector-product/images/tokenRequest4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion adobe-esign-connector/cms/cms_en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ Dialogs:
IntegrationKeyInfo: Setting the integrationKey will disable OAuth. If you wanna use OAuth then fill the OAuth section and leave the integrationKey empty.
OAuth: OAuth
RedirectURI: Redirect URI
RequestToken: Save and Request Token
RequestToken: Save and Request new Token
Title: Adobe Sign Admin Setup
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ public class OAuth2BearerFilter implements javax.ws.rs.client.ClientRequestFilte
private static final String TOKEN_SEPARATOR = ":";
public static final String AUTH_SCOPE_PROPERTY = "AUTH.scope";

public static final AdobeVariable TOKEN_VAR = AdobeVariable.OAUTH_TOKEN;
public static final AdobeVariable REFRESH_TOKEN_VAR = AdobeVariable.OAUTH_TOKEN;
public static final AdobeVariable ACCESS_TOKEN_VAR = AdobeVariable.ACCESS_TOKEN;

private String property;
private Supplier<String> name = null;
Expand All @@ -59,22 +60,38 @@ public void filter(ClientRequestContext context) throws IOException {

protected final String getAccessToken(ClientRequestContext context) {
FeatureConfig config = new FeatureConfig(context.getConfiguration(), getSource());
VarTokenStore store = VarTokenStore.get(TOKEN_VAR.getVariableName());
var token = store.getToken();
if (token == null || token.isExpired()) {
if (token == null || !token.hasRefreshToken()) {
token = getNewAccessToken(context.getClient(), config);
} else {
token = getRefreshedAccessToken(context.getClient(), config, token.refreshToken());
VarTokenStore refreshTokenStore = VarTokenStore.get(REFRESH_TOKEN_VAR.getVariableName());
var refreshToken = refreshTokenStore.getToken();

VarTokenStore accessTokenStore = VarTokenStore.get(ACCESS_TOKEN_VAR.getVariableName());
var accessToken = accessTokenStore.getToken();

String resultToken = null;

if(accessToken == null || accessToken.isExpired()) {
// refresh access token
if(refreshToken != null && refreshToken.hasRefreshToken()) {
accessToken = getRefreshedAccessToken(context.getClient(), config, refreshToken.refreshToken());
accessTokenStore.setToken(accessToken);
resultToken = accessToken.accessToken();
}
store.setToken(token); // keep for following requests
else { // get new access token
refreshToken = getNewAccessToken(context.getClient(), config);
refreshTokenStore.setToken(refreshToken);
accessTokenStore.setToken(refreshToken);
resultToken = refreshToken.accessToken();
}
}
else { // use existing token
resultToken = accessToken.accessToken();
}
if (!token.hasAccessToken()) {
store.setToken(null);
authError().withMessage("Failed to read 'access_token' from " + token).throwError();

if (accessToken != null && !accessToken.hasAccessToken()) {
accessTokenStore.setToken(null);
authError().withMessage("Failed to read 'access_token' from " + refreshToken).throwError();
}

return token.accessToken();
return resultToken;
}

String createKey(FeatureConfig config) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public enum AdobeVariable {
USE_USER_PASS_FLOW_USER("adobe-sign-connector.useUserPassFlow.user"),
USE_USER_PASS_FLOW_PASS("adobe-sign-connector.useUserPassFlow.pass"),
OAUTH_TOKEN("adobe-sign-connector.oauthToken"),
ACCESS_TOKEN("adobe-sign-connector.accessToken"),
AUTHENTICATION_URI("adobe-sign-connector.authenticationUri");

private String variableName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

import com.axonivy.connector.adobe.esign.connector.AdminSetup.AdminSetupData;
import com.axonivy.connector.adobe.esign.connector.auth.OAuth2Feature.AccessTokenRequest;
import com.axonivy.connector.adobe.esign.connector.auth.OAuth2Feature.RefreshTokenRequest;
import com.axonivy.connector.adobe.esign.connector.auth.oauth.OAuth2UriProperty;
import com.axonivy.connector.adobe.esign.connector.auth.oauth.Token;
import com.axonivy.connector.adobe.esign.connector.auth.oauth.VarTokenStore;
Expand All @@ -45,6 +44,7 @@ public static void initVars(AdminSetupData data) {
data.setOauthToken(AdobeVariable.OAUTH_TOKEN.getValue());
data.setPermissions(AdobeVariable.PERMISSIONS.getValue());
data.setReturnPage(AdobeVariable.RETURN_PAGE.getValue());
data.setAccessToken(AdobeVariable.ACCESS_TOKEN.getValue());
}

/**
Expand All @@ -60,8 +60,7 @@ public static void updateVars(AdminSetupData data) {
AdobeVariable.INTEGRATION_KEY.updateValue(data.getIntegrationKey());
AdobeVariable.PERMISSIONS.updateValue(data.getPermissions());
AdobeVariable.RETURN_PAGE.updateValue(data.getReturnPage());
// dont updateW oauth token variable
AdobeVariable.OAUTH_TOKEN.updateValue(data.getOauthToken());
// dont update oauth token variables
}

/**
Expand Down Expand Up @@ -100,27 +99,29 @@ public static void getNewAccessToken(String authCode) throws URISyntaxException
var clientSecret = AdobeVariable.CLIENT_SECRET.getValue();
UriBuilder tokenBaseUri = UriBuilder.fromUri(new URI(AdobeVariable.BASE_URI.getValue()));

VarTokenStore store = VarTokenStore.get(AdobeVariable.OAUTH_TOKEN.getVariableName());
Token token = store.getToken();
// get token stores for refresh and access tokens
VarTokenStore refreshTokenStore = VarTokenStore.get(AdobeVariable.OAUTH_TOKEN.getVariableName());
VarTokenStore accessTokenStore = VarTokenStore.get(AdobeVariable.ACCESS_TOKEN.getVariableName());

//request new token
Client client = ClientBuilder.newClient();
Response response = null;
if (token == null || !token.hasRefreshToken()) {
tokenBaseUri.path(OAuth2UriProperty.TOKEN_RELATIVE_PATH);
AccessTokenRequest authRequest = new AccessTokenRequest(authCode, clientId, clientSecret,
createRedirectUrl());
WebTarget target = client.target(tokenBaseUri);
response = target.request().post(Entity.form(authRequest.paramsMap()));
} else {
tokenBaseUri.path(OAuth2UriProperty.REFRESH_RELATIVE_PATH);
RefreshTokenRequest authRequest = new RefreshTokenRequest(token.refreshToken(), clientId, clientSecret);
WebTarget target = client.target(tokenBaseUri);
response = target.request().post(Entity.form(authRequest.paramsMap()));
}
tokenBaseUri.path(OAuth2UriProperty.TOKEN_RELATIVE_PATH);
AccessTokenRequest authRequest = new AccessTokenRequest(authCode, clientId, clientSecret,
createRedirectUrl());
WebTarget target = client.target(tokenBaseUri);
Response response = target.request().post(Entity.form(authRequest.paramsMap()));
// else {
// tokenBaseUri.path(OAuth2UriProperty.REFRESH_RELATIVE_PATH);
// RefreshTokenRequest authRequest = new RefreshTokenRequest(refreshToken.refreshToken(), clientId, clientSecret);
// WebTarget target = client.target(tokenBaseUri);
// response = target.request().post(Entity.form(authRequest.paramsMap()));
// }
if (response.getStatusInfo().getFamily() == Family.SUCCESSFUL) {
GenericType<Map<String, Object>> map = new GenericType<>(Map.class);
// store new token
Token newToken = new Token(response.readEntity(map));
store.setToken(newToken);
refreshTokenStore.setToken(newToken);
accessTokenStore.setToken(newToken);
} else {
Ivy.log().error("failed to get access token: " + response);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,17 @@
actionListener="#{logic.requestToken}"/>
</div>
</div>
<div class="p-field p-grid p-align-baseline">
<div class="p-col-12 p-md-6">
<p:outputLabel for="accessToken"
value="#{adminSetupBean.getVariableName('ACCESS_TOKEN')}" />
</div>
<div class="p-col-12 p-md-6">
<p:inputText id="accessToken" value="#{data.accessToken}"
readonly="true" />
<p:tooltip for="accessToken" value="#{data.accessToken}" position="top"/>
</div>
</div>
<div class="p-field p-grid p-align-baseline">
<div class="p-col-12 p-md-6">
<p:outputLabel for="redirectUri"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ returnPage String #field
returnPage PERSISTENT #fieldModifier
oauthToken String #field
oauthToken PERSISTENT #fieldModifier
accessToken String #field
accessToken PERSISTENT #fieldModifier

0 comments on commit b2a70e6

Please sign in to comment.