diff --git a/docs/config.md b/docs/config.md index 9a0b822..6d2a559 100644 --- a/docs/config.md +++ b/docs/config.md @@ -1,6 +1,6 @@ -# Integrate WSO2 API Store with an external IAM using the Okta OAuth Authorization Server +# Integrate WSO2 API Manager 3.0.0 with an external IAM using the Okta OAuth Authorization Server -In this guide, we explain how to integrate the WSO2 API Store with an external Identity and Access Management server (IAM) using the Okta OAuth Authorization Server +In this guide, we explain how to integrate the WSO2 API Manager with an external Identity and Access Management server (IAM) using the Okta OAuth Authorization Server to manage the OAuth clients and tokens required by WSO2 API Manager. This is a sample client implementation that consumes APIs exposed by Okta OAuth. ## Follow the instructions below to configure the third-party Key Manager @@ -11,7 +11,7 @@ Create an Okta developer account. Get the Instance URL, authorization server ID, 1. Go to the [Okta sign up page](https://developer.okta.com/signup/). Create an Okta account and get the Okta instance URL. - E.g., [https://dev-76342239.oktapreview.com](https://dev-76342239.oktapreview.com) + E.g., [https://dev-735404.okta.com](https://dev-735404.okta.com) ![alt text](images/signup.png) @@ -22,13 +22,19 @@ Create an Okta developer account. Get the Instance URL, authorization server ID, 2. Create a new authorization server. Alternatively, you can use the default server. ![alt text](images/authorization_server.png) + 3. Add a default scope. For that select the authorization server (ex: default) and go to the **Scopes** tab and create a new scope (say default). Select the default tick. + ![alt text](images/default_scope.png) + + ![alt text](images/scope_list.png) + 3. Get the API key : - 1. Log in to the created account and go to the **API** tab and select **Tokens**. - ![alt text](images/token.png) + 1. Go to the **Authorization Servers** section in the **API** tab and select the **Tokens** tab. 2. Click **Create Token** and provide the name for the token. 3. After successful token creation, copy the Token value for the further use. + ![alt text](images/token.png) + 4. Create Access Policies : If you already have at least one access policy in your authorization server, skip the following steps and go to `step 1: (5)`. 1. In the Okta Developer Dashboard, navigate to **API > Authorization Servers**. @@ -47,82 +53,87 @@ Create an Okta developer account. Get the Instance URL, authorization server ID, 4. Enter the requested information. 5. Click **Create Rule** to save the rule. - +6. Create OAuth application to get credentials to access the introspect api: + + 1. Go to the **Applications** tab and select **Add Application** button. + 2. Select **Web** type in the list and select next + ![alt text](images/add_application_intro.png) + 3. Under the **Grant type allowed** section, tick **Client Credentials** + 4. Once app is created, note down the **Client ID** and **Client secret** under **Client Credentials** section. ### Step 2: Configure WSO2 API Manager -1. Download latest OKTA-OAuth-Client-x.x.x.jar from [here](https://github.com/wso2-extensions/apim-keymanager-okta/releases). +1. Download latest OKTA-OAuth-Client-2.x.x.jar from [here](https://github.com/wso2-extensions/apim-keymanager-okta/releases). 2. Copy that JAR file into the `/repository/components/lib` directory. -3. Uncomment the `` parameter in the `/repository/conf/api-manager.xml` file. -Change the values based on your third-party API. ->> **Note :** Replace the value of the `` parameter with the client registration endpoint that you obtained in `step 1: (1)`. Replace the value of the `` parameter with the API key obtained in `step 1: (3)`. +3. Uncomment or add the following parameters in the `repository/conf/deployment.toml` file. Change the values based on your third-party API. ->> **Note :** The `org.wso2.okta.client.OktaOAuthClient` class, mentioned in the following example, extends the Key Manager interface. + ```xml + [apim.oauth_config] + set_jwt_as_opaque_token = true ->> **Note :** Create a common application using `step 3: (3) & (4)`. Obtain the common client id and secret then update the `` and ``. This is only used to authenticate with the introspect endpoint. + [apim.key_manager] + key_manager_client_impl = "org.wso2.okta.client.OktaOAuthClient" + key_validation_handler_impl = "org.wso2.okta.client.OktaKeyValidationHandler" -```xml - - org.wso2.okta.client.OktaOAuthClient - - https://dev-76342239.oktapreview.com - default - 00eka_bXcTzsssaaa4W1dvAIbqcSDdmdKqw1 - 0oadecu24ftznggfL0h7 - DpqHUmldfdJVR8kpRhSRB_wpKQg - - -``` -4. The API Store sub theme is re-written to change the UI for this scenario. Follow the steps below to configure the UI : - 1. Copy the `locale_default.json` file from [here](https://github.com/wso2-extensions/apim-keymanager-okta/blob/OKTA-OAuth-Client-1.0.0/src/main/resources/locale_default.json) and paste it into the `/repository/deployment/server/jaggeryapps/store/site/conf/locales/jaggery` directory. - 2. Copy the `okta` theme folder from [here](https://github.com/wso2-extensions/apim-keymanager-okta/tree/OKTA-OAuth-Client-1.0.0/src/main/resources/okta) - 3. Paste the `okta` theme folder into the `/repository/deployment/server/jaggeryapps/store/site/themes/wso2/subthemes` directory. - 4. Go to the `/repository/deployment/server/jaggeryapps/store/site/conf` directory. Edit the `site.json` file as shown below. - - ```json - { - "theme" : { - "base" : "wso2", - "subtheme" : "okta" - }, - ``` + [apim.key_manager.configuration] + oktaInstanceUrl = "https://dev-735404.okta.com" + defaultScope = "default" + authorizationServerId = "default" + apiKey = "xxxxxxxxxxxxxxxxxxxx" + client_id = "0oa2b1ir5x9qbp5AS4x6" + client_secret = "xxxxxxxxxxxxxxxxxxx" + ``` - The default `site.json` will be as follows, - - ```json - { - "theme" : { - "base" : "wso2" - }, - ``` + | Element | Description | + | ------------- |-------------| + |**oktaInstanceUrl** | Url generated in the section 1| + |**defaultScope** | Scope defined in the point 3 in section 2| + |**authorizationServerId** | Server id which was created in point 2 in section 2| + |**apiKey** | Token generated in section 3| + |**client_id** | Client id generated from section 6| + |**client_secret** | Client secret generated from section 6 | ### Step 3: Run the sample -You have connected WSO2 API Manager with a third-party Okta authorization server. Let's see how WSO2 API Manager creates OAuth clients at Okta OAuth, when applications are registered in the API Store. +You have connected WSO2 API Manager with a third-party Okta authorization server. Let's see how WSO2 API Manager creates OAuth clients at Okta OAuth, when applications are registered in the Dev Portal. 1. Start **WSO2 API Manager**. -2. **Sign in to the WSO2 API Store :** - 1. Store UI : - ![alt text](images/sign_in.png) - 2. cURL command : - ``` - curl -k -X POST -c cookies https://localhost:9443/store/site/blocks/user/login/ajax/login.jag -d 'action=login&username=admin&password=admin' - ``` +2. **Sign in to the Dev Portal :** + 1. Dev Portal UI : + Sign in using the **SIGN-IN** button at the top right corner. + + 2. Generate access token to access dev portal apis : + follow the steps in [here](http://wso2.github.io/carbon-apimgt/apidocs/store/v1/#guide) to generate an access token to access dev portal apis. For APM 3.0.0 please use v0.16 as the version for /client-registration api. 3. **Create an application :** - 1. Store UI : + 1. Dev Portal UI : - Go to the API Store and click the **Applications tab**. Click **ADD APPLICATION** to create a new application. - ![alt text](images/add_application.png) + Go to the Dev Portal and click the **Applications**. Click **ADD NEW APPLICATION** to create a new application. + 2. cURL command : ``` - curl -k -X POST -b cookies https://localhost:9443/store/site/blocks/application/application-add/ajax/application-add.jag -d 'action=addApplication&application=OktaClientApp&tier=Unlimited&description=&callbackUrl=https://www.google.lk' + curl -k -X POST \ + -H "Authorization: Bearer e3f6a2f4-1b88-3458-8a39-99e54c7d283a" \ + -H "Content-Type: application/json" \ + -d'{ + "name":"OktaAPP", + "throttlingPolicy":"Unlimited", + "description":"Okta sample App", + "tokenType":"OAUTH", + "groups":null, + "attributes":{ + + } + }' https://localhost:9443/api/am/store/v1.0/applications ``` + + **Note** note down the **applicationId** returned in the response. This will be used in the next step 4. **Generate an Application Key :** Register an OAuth client in the Okta authorization server and generate the access token. - 1. Store UI : + + 1. cURL command : - You need to send the specific parameters required by the Okta OAuth Server in `jsonParams` as shown below. + You need to send the specific parameters required by the Okta OAuth Server in `additionalProperties` as shown below. ``` - curl -k -X POST -b cookies https://localhost:9443/store/site/blocks/subscription/subscription-add/ajax/subscription-add.jag -d 'action=generateApplicationKey&application=OktaClientApp&authorizedDomains=ALL&keytype=PRODUCTION&validityTime=3600&callbackUrl=https://www.google.lk&jsonParams={"response_types": "code,token,id_token", "grant_types": "refresh_token,authorization_code,implicit,client_credentials,password","token_endpoint_auth_method": "client_secret_basic","application_type": "web", "tokenGrantType" : "client_credentials", "tokenScope": "scope1,scope2"}' + curl -k -X POST \ + -H "Authorization: Bearer e3f6a2f4-1b88-3458-8a39-99e54c7d283a" -H "Content-Type: application/json" \ + -d '{ + "keyType":"PRODUCTION", + "grantTypesToBeSupported":[ + "refresh_token", + "password", + "client_credentials", + "authorization_code", + "implicit" + ], + "callbackUrl":"https://www.wso2.com", + "validityTime":3600, + "additionalProperties": "{\"response_types\": \"code,token,id_token\", \"grant_types\": \"refresh_token,authorization_code,implicit,client_credentials,password\",\"token_endpoint_auth_method\": \"client_secret_basic\",\"application_type\": \"web\", \"tokenGrantType\" : \"client_credentials\", \"tokenScope\": \"scope1,scope2\"}" + }' https://localhost:9443/api/am/store/v1.0/applications/4f320831-98eb-45a1-99eb-aa4c2b60c03f/generate-keys ``` + **Note** + `additionalProperties` element contains the parameters that need to be passed for the Okta. -5. **Update the existing application :** + **Note** Response for the above request contains the access token issued by Okta. You could note down this value or you could generate it again using Okta token apis (https://developer.okta.com/docs/reference/api/oidc/#token) + ``` + curl -X POST \ + -H "Content-type:application/x-www-form-urlencoded" \ + "https://dev-735404.okta.com/oauth2/default/v1/token" \ + -d "client_id=&client_secret=&grant_type=client_credentials&scope=default" + ``` + +5. **Invoke an API** + 1. Log in to the Publisher portal and publish an API. + 2. Log in to the Dev portal and subscribe the API to the previously created Application in step 1. + + 3. Invoke the api using the previously generated token. You could use the **Try Out** feature in the Dev Portal to test this + + +6. **Delete an OAuth Application :** To delete an OAuth application in the Okta server, do the following. - 1. Store UI: + 1. Dev Portal UI: - Go to the Applications page in the WSO2 API Store. Click Delete to delete your application. - ![alt text](images/delete_application.png) + Go to the Applications page in the WSO2 Dev Portal. Click Delete to delete your application. + 2. cURL command : ``` - curl -k -X POST -b cookies https://localhost:9443/store/site/blocks/subscription/subscription-add/ajax/subscription-add.jag -d 'action=deleteAuthApplication&consumerKey=0oadf3m45jgYaadbJWb0h7' + curl -k -X DELETE \ + -H "Authorization: Bearer e3f6a2f4-1b88-3458-8a39-99e54c7d283a" \ + https://localhost:9443/api/am/store/v1.0/applications/4f320831-98eb-45a1-99eb-aa4c2b60c03f ``` -8. **Provision an Out-of-Band OAuth Client :** Provision an OAuth client created in the Okta server. +7. **Provision an Out-of-Band OAuth Client :** Provision an OAuth client created in the Okta server. - Enable the option to provide out-of-band keys by opening the `/repository/deployment/server/jaggeryapps/store/site/conf/site.json` file and changing the `"mapExistingAuthApps"` setting to `true`. + Enable the option to provide out-of-band keys by opening the `repository/conf/deployment.toml` file and uncommenting the `#[apim.devportal]` setting to `enable_key_provisioning = true`. - > ``"mapExistingAuthApps" : true` - - 1. Store UI : + ``` + [apim.devportal] + enable_key_provisioning = true + ``` + **Prerequisites** + Create an application in Okta as mentioned in the step 6 in section 1 and get the client id and the client secret. + + 1. Dev portal UI : After creating an application, go to the **Production Keys** tab of the Application. - ![alt text](images/map_application.png) - - Click Provide Keys under Map Existing OAuth Keys. + Go to Provide Keys under Provide Existing OAuth Keys. - ![alt text](images/map_application_details.png) + Fill out the required parameters and click Save. You will be redirected to the page that has application and access token details. + + >>**Note :** If you have not provide consumer secret, the access token will not be generated. >>**Note :** Please make a note of this Consumer Secret and Access Token values, as it will be the only one time that you will be able to view it. - 2. cURL command : - ``` - curl -X POST -b cookies https://localhost:9443/store/site/blocks/subscription/subscription-add/ajax/subscription-add.jag -d 'action=mapExistingOauthClient&applicationName=OktaClientApp&keytype=PRODUCTION&callbackUrl=https://www.google.lk&authorizedDomains=ALL&validityTime=3600&client_id=0oadae8nosfopVl7dA0h7&jsonParams={"username":"admin","key_type":"PRODUCTION","client_secret":"bsdsds7-MN0vivfHLO37VyB9M1P19-Ku2qF8OAHH","validityPeriod":"3600", "tokenScope":"test", "tokenGrantType" : "client_credentials"}' - ``` - -9. **Clean partially-created keys :** - - Clean any partially-created keys from the API Manager database, before adding a new subscription. Partially-created keys can remain in the API Manager databases when an OAuth application of a third-party Okta server gets deleted through the API Store UI. It only deletes the mapping that is maintained within API Manager. - - 1. Store UI : - - Go to the **Production Keys** tab of the Application and click **Clean up**. - - ![alt text](images/cleanup.png) - 2. cURL command : - - ``` - curl -X POST -b cookies https://localhost:9443/store/site/blocks/subscription/subscription-add/ajax/subscription-add.jag -d 'action=cleanUpApplicationRegistration&applicationName=OktaClientApp&keyType=PRODUCTION' - ``` - -10. **Revoke the token and re-generate the access token from the OAuth Provider :** - 1. Store UI : +8. **Revoke the token and re-generate the access token from the OAuth Provider :** + 1. Replace `` with the `Base64 encoded ConsumerKey:ConsumerSecret` of the client application you just created. + ``` + curl -k -d "token=eyJraWQiOiJHTmtDeWd3dklXLTJjV1pGaXNVMkdKa2dXRi1WRk04R2tzeDc4VHZwTU00IiwiYWxnIjoiUlMyNTYifQ.eyJ2ZXIiOjEsImp0aSI6IkFULm5oNmhlNy0yNm1YZUgxc" -H "Authorization: Basic " -H "Content-Type: application/x-www-form-urlencoded" https://dev-76321439.oktapreview.com/oauth2/default/v1/revoke + ``` + 2. Obtain a token from the OAuth Provider. + Replace `` with the `Base64 encoded ConsumerKey:ConsumerSecret` of the client application you just created. + ``` + curl -k -d "grant_type=client_credentials&scope=test" -H "Authorization: Basic " -H "Content-Type: application/x-www-form-urlencoded" https://dev-76321439.oktapreview.com/oauth2/default/v1/token + ``` + \ No newline at end of file diff --git a/docs/images/add_application.png b/docs/images/add_application.png index 5e891a9..f4b5299 100644 Binary files a/docs/images/add_application.png and b/docs/images/add_application.png differ diff --git a/docs/images/add_application_intro.png b/docs/images/add_application_intro.png new file mode 100644 index 0000000..fe2fecb Binary files /dev/null and b/docs/images/add_application_intro.png differ diff --git a/docs/images/authorization_server.png b/docs/images/authorization_server.png index c099d53..cc3adca 100644 Binary files a/docs/images/authorization_server.png and b/docs/images/authorization_server.png differ diff --git a/docs/images/default_scope.png b/docs/images/default_scope.png new file mode 100644 index 0000000..1392dce Binary files /dev/null and b/docs/images/default_scope.png differ diff --git a/docs/images/delete_application.png b/docs/images/delete_application.png index 2ba74dd..5271c2d 100644 Binary files a/docs/images/delete_application.png and b/docs/images/delete_application.png differ diff --git a/docs/images/invoke.png b/docs/images/invoke.png new file mode 100644 index 0000000..fe3564d Binary files /dev/null and b/docs/images/invoke.png differ diff --git a/docs/images/map_application_details.png b/docs/images/map_application_details.png index c499b19..b7fcea6 100644 Binary files a/docs/images/map_application_details.png and b/docs/images/map_application_details.png differ diff --git a/docs/images/map_gen_token.png b/docs/images/map_gen_token.png new file mode 100644 index 0000000..228a61d Binary files /dev/null and b/docs/images/map_gen_token.png differ diff --git a/docs/images/scope_list.png b/docs/images/scope_list.png new file mode 100644 index 0000000..6b985ad Binary files /dev/null and b/docs/images/scope_list.png differ diff --git a/docs/images/sign_in.png b/docs/images/sign_in.png index 38e2b7a..c76cd23 100644 Binary files a/docs/images/sign_in.png and b/docs/images/sign_in.png differ diff --git a/docs/images/signup.png b/docs/images/signup.png index eb466c4..746ac7e 100644 Binary files a/docs/images/signup.png and b/docs/images/signup.png differ diff --git a/docs/images/subscribe.png b/docs/images/subscribe.png new file mode 100644 index 0000000..2fd5472 Binary files /dev/null and b/docs/images/subscribe.png differ diff --git a/docs/images/token.png b/docs/images/token.png index 482dedd..d223d45 100644 Binary files a/docs/images/token.png and b/docs/images/token.png differ diff --git a/pom.xml b/pom.xml index c033652..b79a88c 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.wso2.okta.client OKTA-OAuth-Client - 1.0.3-SNAPSHOT + 2.0.0 bundle Client implementation to integrate with Okta Authorization Server http://wso2.org @@ -133,8 +133,8 @@ maven-compiler-plugin 2.3.2 - 1.6 - 1.6 + 1.8 + 1.8 @@ -178,7 +178,7 @@ - 6.1.66 + 6.5.349 1.1 2.1 diff --git a/src/main/java/org/wso2/okta/client/OktaConstants.java b/src/main/java/org/wso2/okta/client/OktaConstants.java index d9e1edd..90eb29d 100644 --- a/src/main/java/org/wso2/okta/client/OktaConstants.java +++ b/src/main/java/org/wso2/okta/client/OktaConstants.java @@ -65,6 +65,7 @@ public class OktaConstants { public static final String ACCESS_TOKEN_IDENTIFIER = "jti"; public static final String ACCESS_TOKEN_EXPIRES_IN = "expires_in"; public static final String OKTA_INSTANCE_URL = "oktaInstanceUrl"; + public static final String OKTA_DEFAULT_SCOPE = "defaultScope"; public static final String OKTA_AUTHORIZATION_SERVER_ID = "authorizationServerId"; public static final String TOKEN_SCOPE = "tokenScope"; public static final String TOKEN_GRANT_TYPE = "tokenGrantType"; @@ -75,6 +76,7 @@ public class OktaConstants { public static final String STRING_FORMAT = "%s %s"; public static final String ERROR_OCCURRED_WHILE_READ_OR_CLOSE_BUFFER_READER = "Error has occurred while reading " + "or closing buffer reader"; + public static final String ADDITIONAL_PROPERTIES = "additionalProperties"; OktaConstants() { } diff --git a/src/main/java/org/wso2/okta/client/OktaKeyValidationHandler.java b/src/main/java/org/wso2/okta/client/OktaKeyValidationHandler.java new file mode 100644 index 0000000..ca3eac7 --- /dev/null +++ b/src/main/java/org/wso2/okta/client/OktaKeyValidationHandler.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.okta.client; + +import org.wso2.carbon.apimgt.keymgt.APIKeyMgtException; +import org.wso2.carbon.apimgt.keymgt.handlers.DefaultKeyValidationHandler; +import org.wso2.carbon.apimgt.keymgt.service.TokenValidationContext; + +public class OktaKeyValidationHandler extends DefaultKeyValidationHandler{ + + @Override + public boolean validateScopes(TokenValidationContext validationContext) throws APIKeyMgtException { + //Implement scope validation if needed. Currently scope validation is disabled + return true; + } +} diff --git a/src/main/java/org/wso2/okta/client/OktaOAuthClient.java b/src/main/java/org/wso2/okta/client/OktaOAuthClient.java index 0942ae5..a2bab36 100644 --- a/src/main/java/org/wso2/okta/client/OktaOAuthClient.java +++ b/src/main/java/org/wso2/okta/client/OktaOAuthClient.java @@ -44,11 +44,13 @@ import org.wso2.carbon.apimgt.api.model.KeyManagerConfiguration; import org.wso2.carbon.apimgt.api.model.OAuthAppRequest; import org.wso2.carbon.apimgt.api.model.OAuthApplicationInfo; +import org.wso2.carbon.apimgt.api.model.Scope; import org.wso2.carbon.apimgt.api.model.AccessTokenInfo; import org.wso2.carbon.apimgt.api.model.API; import org.wso2.carbon.apimgt.api.model.ApplicationConstants; import org.wso2.carbon.apimgt.impl.APIConstants; import org.wso2.carbon.apimgt.impl.AbstractKeyManager; +import org.wso2.carbon.apimgt.impl.dao.ApiMgtDAO; import org.wso2.carbon.apimgt.impl.factory.KeyManagerHolder; import org.wso2.carbon.utils.multitenancy.MultitenantConstants; @@ -169,7 +171,15 @@ public OAuthApplicationInfo createApplication(OAuthAppRequest oAuthAppRequest) t */ @Override public OAuthApplicationInfo updateApplication(OAuthAppRequest oAuthAppRequest) throws APIManagementException { - Object updateAppInOkta = oAuthAppRequest.getOAuthApplicationInfo().getParameter(OktaConstants.UPDATE_APP_IN_OKTA); + JSONParser parser =new JSONParser(); + JSONObject additionalProperties = null; + try { + additionalProperties = (JSONObject) parser + .parse((String) oAuthAppRequest.getOAuthApplicationInfo().getParameter(OktaConstants.ADDITIONAL_PROPERTIES)); + } catch (ParseException e) { + handleException("Error while parsing additionalProperties."); + } + Object updateAppInOkta = additionalProperties.get(OktaConstants.UPDATE_APP_IN_OKTA); if (updateAppInOkta == null || !Boolean.valueOf(String.valueOf(updateAppInOkta))) { return null; } @@ -335,7 +345,41 @@ public OAuthApplicationInfo retrieveApplication(String clientId) throws APIManag if (statusCode == HttpStatus.SC_OK) { JSONParser parser = new JSONParser(); responseJSON = parser.parse(reader); - return createOAuthAppInfoFromResponse((JSONObject) responseJSON); + //Since /oauth2/v1/clients/{clientId} does not send the client_secret, we do and update request + //with the same body we receive from GET request. This returns the client secret without changing + //any app related properties. (PUT /oauth2/v1/clients/{clientId}) + + String jsonPayload = ((JSONObject)responseJSON).toJSONString(); + if (log.isDebugEnabled()) { + log.debug(String.format("Payload to update an OAuth client : %s for the Consumer Key %s", jsonPayload, + clientId)); + } + HttpPut httpPut = new HttpPut(registrationEndpoint); + httpPut.setEntity(new StringEntity(jsonPayload, OktaConstants.UTF_8)); + httpPut.setHeader(OktaConstants.HTTP_HEADER_CONTENT_TYPE, OktaConstants.APPLICATION_JSON); + // Setting Authorization Header, with API Key. + httpPut.setHeader(OktaConstants.AUTHORIZATION, OktaConstants.AUTHENTICATION_SSWS + apiKey); + if (log.isDebugEnabled()) { + log.debug(String.format("Invoking HTTP request to update client in Okta for Consumer Key %s", clientId)); + } + response = httpClient.execute(httpPut); + statusCode = response.getStatusLine().getStatusCode(); + entity = response.getEntity(); + if (entity == null) { + handleException(String.format(OktaConstants.STRING_FORMAT, OktaConstants.ERROR_COULD_NOT_READ_HTTP_ENTITY, response)); + } + reader = new BufferedReader(new InputStreamReader(entity.getContent(), OktaConstants.UTF_8)); + JSONObject responseObject = getParsedObjectByReader(reader); + if (statusCode == HttpStatus.SC_OK) { + if (responseObject != null) { + return createOAuthAppInfoFromResponse(responseObject); + } else { + handleException("ResponseObject is empty. Can not return oAuthApplicationInfo."); + } + } else { + handleException(String.format("Error occured when updating the Client with Consumer Key %s" + + " : Response: %s", clientId, responseObject.toJSONString())); + } } else { handleException(String.format("Error occured while retrieving client for the Consumer Key %s", clientId)); @@ -379,7 +423,8 @@ public AccessTokenInfo getNewApplicationAccessToken(AccessTokenRequest accessTok parameters.add(new BasicNameValuePair(OktaConstants.GRANT_TYPE, (String) grantType)); String scopeString = convertToString(accessTokenRequest.getScope()); if (StringUtils.isEmpty(scopeString)) { - handleException(String.format("Scope cannot be empty for the Consumer Key %s", clientId)); + parameters.add(new BasicNameValuePair(OktaConstants.ACCESS_TOKEN_SCOPE, + configuration.getParameter(OktaConstants.OKTA_DEFAULT_SCOPE))); } else { parameters.add(new BasicNameValuePair(OktaConstants.ACCESS_TOKEN_SCOPE, scopeString)); } @@ -414,6 +459,7 @@ public AccessTokenInfo getNewApplicationAccessToken(AccessTokenRequest accessTok @Override public AccessTokenRequest buildAccessTokenRequestFromOAuthApp( OAuthApplicationInfo oAuthApplication, AccessTokenRequest tokenRequest) throws APIManagementException { + log.debug("Invoking buildAccessTokenRequestFromOAuthApp() method.."); if (oAuthApplication == null) { return tokenRequest; } @@ -466,6 +512,7 @@ public AccessTokenRequest buildAccessTokenRequestFromOAuthApp( @Override public OAuthApplicationInfo buildFromJSON(OAuthApplicationInfo oAuthApplicationInfo, String jsonInput) throws APIManagementException { + log.debug("Invoking buildFromJSON() method.."); JSONParser parser = new JSONParser(); JSONObject jsonObject; try { @@ -667,6 +714,15 @@ public AccessTokenInfo getAccessTokenByConsumerKey(String s) throws APIManagemen private String createJsonPayloadFromOauthApplication(OAuthApplicationInfo oAuthApplicationInfo, Map paramMap) throws APIManagementException { String clientName = oAuthApplicationInfo.getClientName(); + JSONParser parser =new JSONParser(); + JSONObject additionalProperties = null; + try { + additionalProperties = (JSONObject) parser + .parse((String) oAuthApplicationInfo.getParameter(OktaConstants.ADDITIONAL_PROPERTIES)); + } catch (ParseException e) { + handleException("Error while parsing additionalProperties."); + } + //additionalProperties = JSONParser.pa; if (log.isDebugEnabled()) { log.debug(String.format("Creating json payload from Oauth application info for the application: %s", clientName)); @@ -682,7 +738,7 @@ private String createJsonPayloadFromOauthApplication(OAuthApplicationInfo oAuthA List redirectUris = Collections.singletonList(clientRedirectUri); paramMap.put(OktaConstants.CLIENT_REDIRECT_URIS, redirectUris); - Object clientResponseTypes = oAuthApplicationInfo.getParameter(OktaConstants.CLIENT_RESPONSE_TYPES); + Object clientResponseTypes = additionalProperties.get(OktaConstants.CLIENT_RESPONSE_TYPES); if (clientResponseTypes != null) { String[] responseTypes = ((String) clientResponseTypes).split(","); JSONArray jsonArray = new JSONArray(); @@ -692,7 +748,7 @@ private String createJsonPayloadFromOauthApplication(OAuthApplicationInfo oAuthA handleException("Mandatory parameter response_types is missing"); } - Object clientGrantTypes = oAuthApplicationInfo.getParameter(OktaConstants.CLIENT_GRANT_TYPES); + Object clientGrantTypes = additionalProperties.get(OktaConstants.CLIENT_GRANT_TYPES); if (clientGrantTypes != null) { String[] grantTypes = ((String) clientGrantTypes).split(","); JSONArray jsonArray = new JSONArray(); @@ -711,7 +767,7 @@ private String createJsonPayloadFromOauthApplication(OAuthApplicationInfo oAuthA paramMap.put(OktaConstants.CLIENT_POST_LOGOUT_REDIRECT_URIS, jsonArray); } - String tokenEndpointAuthMethod = (String) oAuthApplicationInfo.getParameter( + String tokenEndpointAuthMethod = (String) additionalProperties.get( OktaConstants.CLIENT_TOKEN_ENDPOINT_AUTH_METHOD); if (StringUtils.isNotEmpty(tokenEndpointAuthMethod)) { paramMap.put(OktaConstants.CLIENT_TOKEN_ENDPOINT_AUTH_METHOD, tokenEndpointAuthMethod); @@ -734,7 +790,7 @@ private String createJsonPayloadFromOauthApplication(OAuthApplicationInfo oAuthA paramMap.put(OktaConstants.CLIENT_INITIATE_LOGIN_URI, initiateLoginUri); } - String applicationType = (String) oAuthApplicationInfo.getParameter(OktaConstants.CLIENT_APPLICATION_TYPE); + String applicationType = (String) additionalProperties.get(OktaConstants.CLIENT_APPLICATION_TYPE); if (StringUtils.isNotEmpty(applicationType)) { paramMap.put(OktaConstants.CLIENT_APPLICATION_TYPE, applicationType); } else { @@ -786,17 +842,19 @@ private OAuthApplicationInfo createOAuthAppInfoFromResponse(Map responseMap) { appInfo.addParameter(OktaConstants.CLIENT_RESPONSE_TYPES, responseTypes); Object grantTypes = responseMap.get(OktaConstants.CLIENT_GRANT_TYPES); - appInfo.addParameter(OktaConstants.CLIENT_GRANT_TYPES, grantTypes); + appInfo.addParameter(OktaConstants.CLIENT_GRANT_TYPES, String.join(" ", (JSONArray) grantTypes)); Object tokenEndpointAuthMethod = responseMap.get(OktaConstants.CLIENT_TOKEN_ENDPOINT_AUTH_METHOD); appInfo.addParameter(OktaConstants.CLIENT_TOKEN_ENDPOINT_AUTH_METHOD, tokenEndpointAuthMethod); Object initiateLoginUri = responseMap.get(OktaConstants.CLIENT_INITIATE_LOGIN_URI); appInfo.addParameter(OktaConstants.CLIENT_INITIATE_LOGIN_URI, initiateLoginUri); + + appInfo.addParameter(OktaConstants.ADDITIONAL_PROPERTIES, responseMap.toString()); return appInfo; } - + /** * Revokes an access token. * @@ -1043,15 +1101,17 @@ private static void handleException(String msg) throws APIManagementException { throw new APIManagementException(msg); } - /** - * Common method to throw exceptions. - * - * @param msg this parameter contain error message that we need to throw. - * @param e Exception object. - * @throws APIManagementException This is the custom exception class for API management - */ - private static void handleException(String msg, Exception e) throws APIManagementException { - log.error(msg, e); - throw new APIManagementException(msg, e); + @Override + public String getNewApplicationConsumerSecret(AccessTokenRequest arg0) throws APIManagementException { + log.warn("Consumer key updating is not supported"); + return null; + } + + @Override + public Map> getScopesForAPIS(String apiIdsString) throws APIManagementException { + log.debug("Invoking getScopesForAPIS() method for apiId " + apiIdsString); + ApiMgtDAO apiMgtDAO = ApiMgtDAO.getInstance(); + Map> scopes = apiMgtDAO.getScopesForAPIS(apiIdsString); + return scopes; } }