diff --git a/en/identity-server/7.0.0/docs/assets/img/references/add-access-url-to-app.png b/en/identity-server/7.0.0/docs/assets/img/references/add-access-url-to-app.png new file mode 100644 index 0000000000..9ea5f57a17 Binary files /dev/null and b/en/identity-server/7.0.0/docs/assets/img/references/add-access-url-to-app.png differ diff --git a/en/identity-server/7.0.0/docs/references/extend/authentication/conditional-auth/write-custom-functions-for-conditional-authentication.md b/en/identity-server/7.0.0/docs/references/extend/authentication/conditional-auth/write-custom-functions-for-conditional-authentication.md index 33ef852e79..b07c5cf73f 100644 --- a/en/identity-server/7.0.0/docs/references/extend/authentication/conditional-auth/write-custom-functions-for-conditional-authentication.md +++ b/en/identity-server/7.0.0/docs/references/extend/authentication/conditional-auth/write-custom-functions-for-conditional-authentication.md @@ -1,127 +1 @@ -# Write Custom Functions for Conditional Authentication - -With conditional authentication, it is possible to configure dynamic sequences based on runtime parameters such as the user’s IP address, user role, etc. in the WSO2 Identity Server. This feature allows you to define a dynamic authentication sequence using authentication scripts written in JavaScript. For more information, see [Add conditional authentication]({{base_path}}/guides/authentication/conditional-auth). - -Custom functions that expose any useful WSO2 Identity Server core functions and any utility functions can be added as OSGi services. A core set of functions are available at the following GitHub location: - - -See [Conditional authentication - API reference]({{base_path}}/references/conditional-auth/api-reference) for more information. - ----- - -## Write the function -The following steps provide some guidelines on how you can write custom functions for conditional authentication. - -1. Create an Apache Maven module that has the packaging type as `bundle` and add the `maven-bundle-plugin` configuration. - - !!! info - You may skip this step if you are adding a function to an existing component providing a related functionality. - -2. Create a functional interface (i.e., an Interface with a single public method). The method name and parameters should be the same as the ones you provide from `js`. You may need to use wrapper classes for any object classes (except `number`, `string`, and `boolean`). - - !!! info - - You may skip this step if you can use an existing object. - - For more information on objects, see [object reference documentation]({{base_path}}/references/conditional-auth/api-reference/#object-reference). - - - The JavaScript function is as follows: - - ``` js - var isBar = barMethod(context, "s2", {}); - ``` - - - The functional interface is as follows: - - ``` java - @FunctionalInterface - public interface FooFunction { - - boolean barMethod(JsAuthenticationContext context, String s2, CustomJsObject object); - } - ``` - -3. Create a class that implements the functional interface in the above step and implement your logic. - - ``` java - public class FooFunctionImp implements FooFunction { - boolean barMethod(String s1, String s2, CustomJsObject object) { - //Implementation - } - } - ``` - - !!! note - It is recommended to throw errors from the Java methods to the authentication script. All the errors have to be handled by the method itself. - -4. Add `JsFunctionRegistry` service to the service component class. - - ``` java - @Reference( - service = JsFunctionRegistry.class, - cardinality = ReferenceCardinality.MANDATORY, - policy = ReferencePolicy.DYNAMIC, - unbind = "unsetJsFunctionRegistry" - ) - public void setJsFunctionRegistry(JsFunctionRegistry jsFunctionRegistry) { - FooFunctionsServiceHolder.getInstance().setJsFunctionRegistry(jsFunctionRegistry); - } - - public void unsetJsFunctionRegistry(JsFunctionRegistry jsFunctionRegistry) { - FooFunctionsServiceHolder.getInstance().setJsFunctionRegistry(null); - } - ``` - -5. In the bundle activator of the module, register the class created in step 3 in the `JsFunctionRegistry` service. - - ``` java - @Activate - protected void activate(ComponentContext ctxt) { - - FooFunction fooFunctionImpl = new FooFunctionImpl(); - JsFunctionRegistry jsFunctionRegistry = FooFunctionsServiceHolder.getInstance().getJsFunctionRegistry(); - jsFunctionRegistry.register(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "barMethod", fooFunctionImpl); - } - - @Deactivate - protected void deactivate(ComponentContext ctxt) { - - JsFunctionRegistry jsFunctionRegistry = UserFunctionsServiceHolder.getInstance().getJsFunctionRegistry(); - if (jsFunctionRegistry != null) { - jsFunctionRegistry.deRegister(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "barMethod"); - } - } - ``` - -6. If you have one class that implements many functional interfaces, you need to cast to that particular functional interface when registering. - - ``` java - jsFunctionRegistry.register(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "barMethod", (FooFunction)fooFunctionImpl::barMethod); - ``` - -## Try it out -This section guides you to try out a sample conditional authentication function. - -### Deploy a sample authentication function -1. Build [the sample](https://github.com/wso2/samples-is/tree/v4.6.0/adaptive-authentication/org.wso2.custom.auth.functions){:target="_blank"} using maven `mvn clean install`. -2. Copy the `org.wso2.custom.auth.functions-1.0.0` binary file from `target` directory into `/repository/components/dropins` directory. -3. Restart WSO2 IS. - -### Configure the authentication script -To configure the application with a custom conditional authentication script: - -1. Register an application. -2. Navigate to **Login Flow** tab. -3. Click **Start with default configuration** to define the login flow starting with the `username and password` login. -4. Turn on **Conditional Authentication** by switching the toggle on. You can add the following conditional authentication script using the editor. -5. Click **Update**. - ``` js - var onLoginRequest = function(context) { - executeStep(1, { - onSuccess: function (context) { - var userName = getUsernameFromContext(context, 1); - Log.info("Username: " + userName); - } - }); - }; - ``` - -This custom `getUsernameFromContext()` function can be used to retrieve the username from the authentication context. +{% include "../../../../../../../includes/references/extend/authentication/conditional-auth/write-custom-functions-for-conditional-authentication.md" %} \ No newline at end of file diff --git a/en/identity-server/7.0.0/docs/references/extend/authentication/customize-the-authentication-endpoint.md b/en/identity-server/7.0.0/docs/references/extend/authentication/customize-the-authentication-endpoint.md new file mode 100644 index 0000000000..3ab6e62d55 --- /dev/null +++ b/en/identity-server/7.0.0/docs/references/extend/authentication/customize-the-authentication-endpoint.md @@ -0,0 +1 @@ +{% include "../../../../../../includes/references/extend/authentication/customize-the-authentication-endpoint.md" %} \ No newline at end of file diff --git a/en/identity-server/7.0.0/docs/references/extend/authentication/oauth2/write-a-custom-oauth-2.0-grant-type.md b/en/identity-server/7.0.0/docs/references/extend/authentication/oauth2/write-a-custom-oauth-2.0-grant-type.md index 29c4567277..7251dde095 100644 --- a/en/identity-server/7.0.0/docs/references/extend/authentication/oauth2/write-a-custom-oauth-2.0-grant-type.md +++ b/en/identity-server/7.0.0/docs/references/extend/authentication/oauth2/write-a-custom-oauth-2.0-grant-type.md @@ -1,337 +1 @@ -# Write a Custom OAuth 2.0 Grant Type - -OAuth 2.0 authorization servers provide support for four main grant -types according to the [OAuth 2.0 specification](https://tools.ietf.org/html/rfc6749){:target="_blank"}. It -also has the flexibility to support any custom grant types. This topic -provides instructions on how to implement a custom grant type for OAuth -2.0 authorization server and how to extend the behavior of default grant -types. - -The WSO2 Identity Server is used as the OAuth 2.0 authorization server -implementation, which is an open source implementation. - - -## Implement a new grant type - -Follow the steps given below to implement a new grant type. - -1. Implement the following two extensions. - - ` GrantTypeHandler ` - This is the - implementation of the grant type. Here you can implement the - way, it must be validated and how token must be issued. You can - write the new implementation by implementing the - ` AuthorizationGrantHandler ` interface - or by extending - ` AbstractAuthorizationGrantHandler ` . In - most cases, it is enough to extend the - ` AbstractAuthorizationGrantHandler ` in - the WSO2 OAuth component. - - ` GrantTypeValidator ` - This is used to - validate the grant request that is sent to the - ` /token ` endpoint. You can define what - parameters must be in the request and define the validation of - them. You can write the new implementation by extending the - ` AbstractValidator ` in Apache Amber - component. -2. When implementation is done, package your class as a .jar file and - place it in the - ` /repository/component/lib ` directory. -3. To register the custom grant type, configure the - ` /repository/conf/deployment.toml ` - file by adding a new entry, in a manner similar to the following - example; - - ```toml - [[oauth.custom_grant_type]] - name="grant type identifier" - grant_handler="full qualified class name of grant handler" - grant_validator="full qualified class name of grant validator" - [oauth.custom_grant_type.properties] - IdTokenAllowed=true - PublicClientAllowed=true - ``` - - !!! info - Setting the ` ` parameter to - ` true `, provides flexibility to control the - issuing of IDtoken for each grant, and also allows the OIDC scope - validator to validate the grant types that should support the openid - scope. - - Setting the ` ` parameter to - ` true `, provides the capability to configure - whether the grant type can be used by public clients or not. Failing to - set this parameter will result in the grant type being not supported for - public clients. - -4. Restart the server to apply changes. - -To test this out, follow the instructions below to implement a custom-grant type sample. - -## Use the grant type sample - -The sample demonstrated here defines a new sample grant type called the -"mobile" grant type. It is similar to the password grant type and the -only difference is that a mobile number will be passed through instead -of a password. The request to the ` /token ` API must -contain the following two request parameters. - -- ` grant_type=mobile ` -- ` mobileNumber=044322433 ` - -You can access the new grant type project sample from -[here](https://github.com/wso2/samples-is/tree/v4.6.0/oauth2/custom-grant){:target="_blank"} -. The grant handler and validator class is inside the -` org.wso2.sample.identity.oauth2.grant.mobile ` package. -This can be modified as required. - -### Configure the resource - -The following Maven buildable source is compatible with WSO2 Identity -Server 5.1.0 onwards. The attached ` .jar ` file can be -directly used as well. - -| Buildable source | [custom-grant](https://github.com/wso2/samples-is/tree/v4.6.0/oauth2/custom-grant){:target="_blank"} | -|--------------------|------------------------------------------------------------------------------| -| Built jar file | [custom-grant-4.5.6.jar]({{base_path}}/assets/attachments/custom-grant-4.5.6.jar){:target="_blank"} | - -!!! note - To generate the .jar file from the buildable source, run the following Apache Maven - command in the sample's location using the command line. - - ``` java - mvn clean install - ``` - -1. Copy the .jar file in target directory into the - ` /repository/component/lib ` - directory. -2. Configure the following in the -` /repository/conf/deployment.toml ` - file. - ```toml - [[oauth.custom_grant_type]] - name="mobile" - grant_handler="org.wso2.sample.identity.oauth2.grant.mobile.MobileGrant" - grant_validator="org.wso2.sample.identity.oauth2.grant.mobile.MobileGrantValidator" - [oauth.custom_grant_type.properties] - IdTokenAllowed=true - PublicClientAllowed=true - ``` - -3. Restart the server. - -### Configure an application with the custom grant type - -1. On the WSO2 Identity Server Console, go to **Applications**. - -2. Click **New Application** and select **Standard-Based Application**. - -3. Provide an application name and select **OIDC** as the protocol. - -4. Click **Register** to complete the registration. - -5. In the **Protocol** tab, select the custom grant type (mobile) from the **Allowed Grant Types** list. - -6. Click **Update**. Note the **Client ID** and **Client secret** that appear. - - !!! tip - To configure more advanced configurations, see [OIDC Configurations]({{base_path}}/references/app-settings/oidc-settings-for-app). - -### Obtain an access token - -Send the grant request to the `/token` API using a cURL command. - -!!! note - The HTTP POST body must contain the following two parameters: `grant_type=mobile` and `mobileNumber`. - - ``` - grant_type=mobile&mobileNumber= - ``` - -!!! abstract "" - **Request format** - ``` - curl --user : -k -d "grant_type=&mobileNumber=" -H "Content-Type: application/x-www-form-urlencoded" https://:/oauth2/token - ``` - --- - **Sample request** - ```curl - curl --user 7wYeybBGCVfLxPmS0z66WNMffyMa:WYfwHUsbsEvwtqmDLuaxF_VCQJwa -k -d "grant_type=mobile&mobileNumber=0333444" -H "Content-Type: application/x-www-form-urlencoded" https://localhost:9443/oauth2/token - ``` - -You will receive a response similar to the following JSON response with the access token. - -``` -{ - "access_token": "d9b46507-0300-4a8e-839f-0b856a74c481", - "refresh_token": "45a82bd4-ef55-4368-bc20-9cbc240308d1", - "token_type": "mobile", - "expires_in": 10000 -} -``` - -!!! note "Note" - The access token and the refresh token that are received with the response are just randomly generated by the sample application. Those tokens are not valid tokens. - -## Customize an existing grant type - -As an alternative to creating a custom OAuth grant type, you can -customize one of the existing grant types. The following two classes are -sample implementations of customizing the password grant type in -particular but any other grant type can be customized as well. - -- [RoleBasedPasswordGrant](https://github.com/wso2/samples-is/blob/v4.6.0/oauth2/custom-grant/src/main/java/org/wso2/sample/identity/oauth2/grant/password/RoleBasedPasswordGrant.java){:target="_blank"} - - This does some RBAC validation apart from the authentication before - granting access. -- [ModifiedAccessTokenPasswordGrant](https://github.com/wso2/samples-is/blob/v4.6.0/oauth2/custom-grant/src/main/java/org/wso2/sample/identity/oauth2/grant/password/ModifiedAccessTokenPasswordGrant.java){:target="_blank"} - - This implementation customized the access token value. - -### Configure the resource - -1. Copy the .jar file into the `/repository/component/lib` directory. You can also modify the project and build it using Apache Maven 3. -2. Configure the following in the `/repository/conf/deployment.toml` file. - ```toml - [oauth.grant_type.password] - grant_handler="org.wso2.sample.identity.oauth2.grant.password.ModifiedAccessTokenPasswordGrant" - ``` - -3. Restart the server. - -### Configure an application with the customized password grant type - -1. On the WSO2 Identity Server Console, go to **Applications**. - -2. Click **New Application** and select **Standard-Based Application**. - -3. Provide an application name and select **OIDC** as the protocol. - -4. Click **Register** to complete the registration. - -5. In the **Protocol** tab, select the **Password** grant type from the **Allowed Grant Types** list. - -6. Click **Update**. Note the **Client ID** and **Client secret** that appear. - - !!! tip - To configure more advanced configurations, see [OIDC Configurations]({{base_path}}/references/app-settings/oidc-settings-for-app). - -### Obtain an access token - -Send the password grant request to the `/token` API using a cURL command. - -!!! abstract - **Request format** - ``` - curl --user : -k -d "grant_type=password&username=&password=" -H "Content-Type: application/x-www-form-urlencoded" https://:/oauth2/token - ``` - --- - **Sample request** - ```curl - curl --user 7wYeybBGCVfLxPmS0z66WNMffyMa:WYfwHUsbsEvwtqmDLuaxF_VCQJwa -k -d "grant_type=password&username=admin&password=admin" -H "Content-Type: application/x-www-form-urlencoded" https://localhost:9443/oauth2/token - ``` - -You will receive a response similar to the following JSON response with the access token. - -``` -{ - "token_type":"bearer", - "expires_in":2955, - "refresh_token":"6865c8d67b42c0c23e634a8fc5aa81f", - "access_token":"982f40d4-0bb6-41ce-ac5a-1da06a83e475asela@soasecurity.org" -} -``` - -??? note "Sending Custom Error Codes" - - You can send custom - error codes to the client side in case of defined/identified errors, - using a custom grant handler. - - This simply uses the mobile grant sample and adds the specific error - codes/messages to it. - - !!! tip - See [here](#use-the-grant-type-sample) for instructions on how to implement and use the mobile grant - sample before adding the custom error codes to it. - - In the downloaded Maven [buildable source](https://github.com/wso2/samples-is/tree/v4.6.0/oauth2/custom-grant){:target="_blank"}, the following code segment is added in the sample class - ` org .wso2.sample.identity.oauth2.grant.mobile.MobileGrant ` - inside ` validateGrant() ` method, which is the relevant - code used for this mechanism. - - ``` java - if(mobileNumber != null) { - //validate mobile number - authStatus = isValidMobileNumber(mobileNumber); - - if(authStatus) { - // if valid set authorized mobile number as grant user - AuthenticatedUser mobileUser = new AuthenticatedUser(); - mobileUser.setUserName(mobileNumber); - oAuthTokenReqMessageContext.setAuthorizedUser(mobileUser); - oAuthTokenReqMessageContext.setScope(oAuthTokenReqMessageContext.getOauth2AccessTokenReqDTO().getScope()); - } else{ - ResponseHeader responseHeader = new ResponseHeader(); - responseHeader.setKey("SampleHeader-999"); - responseHeader.setValue("Provided Mobile Number is Invalid."); - oAuthTokenReqMessageContext.addProperty("RESPONSE_HEADERS", new ResponseHeader[]{responseHeader}); - } - - } - ``` - - !!! note - - The `ResponseHeader` code chunk sets a custom response header in case an - invalid mobile number is sent. - - Build the project and copy the modified jar to the `/repository/component/lib` directory. - - **Try it out** - - - Happy Path - - ``` powershell - curl --user : -k -d "grant_type=mobile&mobileNumber=0333444" -H "Content-Type: application/x-www-form-urlencoded" https://:/oauth2/token - ``` - - - Erroneous Path - - ``` powershell - curl -v --user : -k -d "grant_type=mobile&mobileNumber=0363444" -H "Content-Type: application/x-www-form-urlencoded" https://:/oauth2/token - ``` - - !!! info - This is erroneous according to the mobile custom grant sample because - the mobile number does not start with '003'. You can use the -v option - in the cURL command to see the header of the response which should be as - seen in the code block below. - - ``` powershell - > POST /oauth2/token HTTP/1.1 - > Authorization: Basic dlNmZVE5amZOb2RZMXR2OUtMTk54TE93N2t3YTpDRVVXdTdmRE55X1JZZzVsT19tcDhQTGY3blFh - > User-Agent: curl/7.29.0 - > Host: localhost:9443 - > Accept: */* - > Content-Type: application/x-www-form-urlencoded - > Content-Length: 38 - > - * upload completely sent off: 38 out of 38 bytes - < HTTP/1.1 400 Bad Request - < Date: Wed, 13 Jan 2016 06:05:33 GMT - < SampleHeader-999: Provided Mobile Number is Invalid. - < Content-Type: application/json - < Content-Length: 87 - < Connection: close - < Server: WSO2 Carbon Server - < - * Closing connection 0 - * SSLv3, TLS alert, Client hello (1): - {"error":"invalid_grant","error_description":"Provided Authorization Grant is invalid"} - ``` - - !!! info - `SampleHeader-999...` shows the custom header appearing in the headers. - - Similarly this can be used to transfer any custom information to the - client, in a flexible manner. +{% include "../../../../../../../includes/references/extend/authentication/oauth2/write-a-custom-oauth-2.0-grant-type.md" %} \ No newline at end of file diff --git a/en/identity-server/7.0.0/docs/references/extend/authentication/write-a-custom-local-authenticator.md b/en/identity-server/7.0.0/docs/references/extend/authentication/write-a-custom-local-authenticator.md index cff76b43f6..00e5c07662 100644 --- a/en/identity-server/7.0.0/docs/references/extend/authentication/write-a-custom-local-authenticator.md +++ b/en/identity-server/7.0.0/docs/references/extend/authentication/write-a-custom-local-authenticator.md @@ -1,146 +1 @@ -# Write a Custom Local Authenticator - -A local authenticator in WSO2 identity server authenticates users, who are stored in the local database, using a defined authentication logic. - -The default local authenticator in WSO2 identity server is the `Username & Password` authenticator. -It authenticates end users stored in a connected user store using the provided username and password. - -WSO2 identity server supports extensibility in local authentication so that you can implement a different authentication logic by writing a custom local authenticator. -You can implement custom authentication logic tailored to your requirement, as follows: - -1. Authenticating users only if they belong to a particular role. -2. Authenticating based on any claim, such as the user's telephone number or employee registration number. -3. Calling another API to authenticate the user. - ---- - -## Scenario - -You have a sample app to which you want users to log in with their telephone numbers instead of usernames. Once a user enters a telephone number, your authentication logic should identify the user and validate the user's credentials. - -The following guide shows you how to write a custom local authenticator to implement this authentication logic. - ---- - -## Write the local authenticator - -To write the local authenticator: - -1. Create a `maven` project for the custom local authenticator. Refer the following files: - - The [pom.xml](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.local.authenticator/pom.xml){:target="_blank"} file used for the sample custom local authenticator. - - The [service component class](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.local.authenticator/src/main/java/org/wso2/carbon/identity/sample/local/authenticator/internal/SampleLocalAuthenticatorServiceComponent.java){:target="_blank"} file to deploy in WSO2 Identity Server and register it as a local authenticator. - -2. Write the [custom local authenticator](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.local.authenticator/src/main/java/org/wso2/carbon/identity/sample/local/authenticator/SampleLocalAuthenticator.java){:target="_blank"} by extending the -[AbstractApplicationAuthenticator](https://github.com/wso2/carbon-identity-framework/blob/v7.0.12/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/AbstractApplicationAuthenticator.java){:target="_blank"} class and implementing the -[LocalApplicationAuthenticator](https://github.com/wso2/carbon-identity-framework/blob/v7.0.12/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/LocalApplicationAuthenticator.java){:target="_blank"} class. - -!!! info - See the sample [custom local authenticator](https://github.com/wso2/samples-is/tree/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.local.authenticator){:target="_blank"} for your reference. - -??? "Methods in `AbstractApplicationAuthenticator` class and `LocalApplicationAuthenticator` interface" - | Method | Description | - |-----------|---------------| - |[**public String getName()**](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.local.authenticator/src/main/java/org/wso2/carbon/identity/sample/local/authenticator/SampleLocalAuthenticator.java#L159){:target="_blank"} | Return the name of the authenticator.

In the sample project, we have used this method to return the name SampleLocalAuthenticator.

| - |[**public String getFriendlyName()**](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.local.authenticator/src/main/java/org/wso2/carbon/identity/sample/local/authenticator/SampleLocalAuthenticator.java#L165){:target="_blank"} | Returns the display name for the custom local authenticator.

In the sample project, we have returned the name sample-local-authenticator.

| - | [**public String getContextIdentifier(HttpServletRequest request**)](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.local.authenticator/src/main/java/org/wso2/carbon/identity/sample/local/authenticator/SampleLocalAuthenticator.java#L153){:target="_blank"} | Returns a unique identifier that will map the authentication request and the response. The value returned by the invocation of the authentication request and the response should be the same. | - | [public boolean canHandle(HttpServletRequest request)](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.local.authenticator/src/main/java/org/wso2/carbon/identity/sample/local/authenticator/SampleLocalAuthenticator.java#L58-L63){:target="_blank"}|

This method checks whether the authentication request is valid, according to the custom authenticator’s requirements. The user will be authenticated if the method returns 'true'. This method also checks whether the authentication or logout request can be handled by the authenticator.

In our sample project, we used this method to check if the username and password are 'not null' in the authentication request. If that succeeds, the authentication flow will continue.

| - | [protected void initiateAuthenticationRequest(HttpServletRequest request,HttpServletResponse response, AuthenticationContext context)](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.local.authenticator/src/main/java/org/wso2/carbon/identity/sample/local/authenticator/SampleLocalAuthenticator.java#L66-L67){:target="_blank"} |

This method is used to redirect the user to the login page to authenticate. You can redirect the user to a custom login URL using this method or you can use the default WSO2 Identity Server login page.

| - | [protected void processAuthenticationResponse(HttpServletRequest request,HttpServletResponse response, AuthenticationContext context)](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.local.authenticator/src/main/java/org/wso2/carbon/identity/sample/local/authenticator/SampleLocalAuthenticator.java#L89-L90){:target="_blank"} |

Implementation of custom authentication logic happens inside this method. For example, you can call any API that can do authentication and authenticate the user or you can authenticate the user against the underlying user store. Then you can also do any other custom logic after authenticating the user such as, you can check if a user belongs to a particular role and allowing authentication accordingly.

In the sample project, we used this method to authenticate the user with the user's telephone number and password, and make the authentication successful.

| - | public AuthenticatorFlowStatus process(HttpServletRequest request, HttpServletResponse response, AuthenticationContext context) |

This method is used to process or carry out the user authentication process. It calls the processAuthenticationResponse() method in the custom authenticator class to execute the custom authentication logic.

In the sample project, we call the super.process(), so that the super class process method will handle the authentication process, instead of implementing our own process method.

- | protected boolean retryAuthenticationEnabled(AuthenticationContext context) |

This method returns a boolean value. If the authentication fails due to some issue like invalid credentials, this method will be called to know whether to retry the authentication flow. If this method returns 'true', the authentication flow will be retried. If returns 'false', the flow is stopped with an exception thrown.

You need to override this method if you are calling the super.process() method. But if you are writing your own process() method, you can handle all the retrying logic accordingly within the method.

| - ---- - -## Deploy the authenticator - -To deploy the custom local authenticator: - -1. Open a terminal, navigate to the root of your project, and compile the service by running the following command: - - ``` xml - mvn clean install - ``` - -2. Copy the generated `org.wso2.carbon.identity.sample.local.authenticator-1.0.0.jar` file from `/target` folder to the `/repository/components/dropins` folder. -3. Add the following config to the deployment.toml file in the `/repository/conf` folder. The name should be the authenticator name returned by the `getName()` method in the authenticator class. - - ``` toml - [authentication.authenticator.sample_authenticator] - name = "SampleLocalAuthenticator" - enable = true - ``` - ---- - -## Configure the Identity Server - -This section guides you on how to configure the identity server to use the custom local authenticator. - -### Prerequisites -- You need to [set up the sample]({{base_path}}/get-started/try-samples/qsg-oidc-webapp-java-ee) application. - -### Configure the application with the custom local authenticator - -To configure the custom local authenticator for the application: - -1. On the WSO2 Identity Server console, go to **Applications**. - -2. Select your application, go to the **Login Flow** tab and add the custom local authenticator from your preferred editor: - - --- - === "Classic Editor" - To add custom local authenticator using the Classic Editor: - - 1. If you haven't already defined a sign-in flow, click **Start with Default configuration** to get started. - - 2. Remove the existing **Username & Password** authenticator and click **Add Authentication** on the step, select your custom local authenticator, and click **Add**. - - ![Add custom federated authenticator]({{base_path}}/assets/img/guides/idp/custom-connector/add-custom-local-authenticator-with-classic-editor.png){: width="700" style="display: block; margin: 0; border: 0.3px solid lightgrey;"} - - === "Visual Editor" - To add custom local authenticator using the Visual Editor: - - 1. Switch to the **Visual Editor** tab, by default the `Username & Password` login flow will be added onto the Visual Editor's workspace. - - 2. Remove the existing **Username & Password** authenticator and click on `+ Add Sign In Option` to add a new authenticator to the same step and select your custom local authenticator. - - ![Add custom local authenticator using the Visual Editor]({{base_path}}/assets/img/guides/idp/custom-connector/add-custom-local-authenticator-with-visual-editor.png){: width="500" style="display: block; margin: 0; border: 0.3px solid lightgrey;"} - - --- - -3. Click **Update** to save your changes. - ---- - -### Add a new user - -To add a new user: - -1. On the WSO2 Identity Server console, go to **User Management** > **Users**. - -2. Click **Add User** and provide the required details. - -3. Add a telephone number to the **Mobile** field and click **Next**. - -5. Add the user to a group, if necessary, and click **Next**. - -6. Assign the user a role, if necessary, and click **Next**. - -7. Review the summary and click **Finish**. - ---- -## Try it out - -1. Access the application URL. - -2. Click **Login**. You will be redirected to the WSO2 Identity Server login page. - -3. Enter the user's **telephone number** as the identifier and enter the user's password, and click **Continue**. - -The user will be successfully authenticated. - ---- - -## Contributing your connectors - -{% include "../../../fragments/contributing-connectors.md" %} +{% include "../../../../../../includes/references/extend/authentication/write-a-custom-local-authenticator.md" %} \ No newline at end of file diff --git a/en/identity-server/7.0.0/docs/references/extend/federation/write-a-custom-federated-authenticator.md b/en/identity-server/7.0.0/docs/references/extend/federation/write-a-custom-federated-authenticator.md index 3f7361a5fd..deed6d8951 100644 --- a/en/identity-server/7.0.0/docs/references/extend/federation/write-a-custom-federated-authenticator.md +++ b/en/identity-server/7.0.0/docs/references/extend/federation/write-a-custom-federated-authenticator.md @@ -1,306 +1 @@ -# Write a Custom Federated Authenticator - -A custom federated authenticator can be written to authenticate a user with an external system. -The external system can be any identity provider such as Facebook, Twitter, Google, Yahoo, etc. -You can use the extension points available in WSO2 Identity Server to create custom federated authenticators. - -![Federated authentication diagram]({{base_path}}/assets/img/references/extend/federated-authentication-diagram.png){: width="1000" style="display: block; margin: 0;"} - ---- - -## Authenticator API - -The following is the API used to implement a custom federated -authenticator. - -??? example "Click to view the API of the Application Authenticators" - ``` java - /* - * Copyright (c) 2005-2013, 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.carbon.identity.application.authentication.framework; - - import java.io.Serializable; - import java.util.List; - - import javax.servlet.http.HttpServletRequest; - import javax.servlet.http.HttpServletResponse; - - import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; - import org.wso2.carbon.identity.application.authentication.framework.exception.AuthenticationFailedException; - import org.wso2.carbon.identity.application.authentication.framework.exception.LogoutFailedException; - import org.wso2.carbon.identity.application.common.model.Property; - - /** - * API of the Application Authenticators. - * - */ - public interface ApplicationAuthenticator extends Serializable { - - /** - * Check whether the authentication or logout request can be handled by the - * authenticator - * - * @param request - * @return boolean - */ - public boolean canHandle(HttpServletRequest request); - - /** - * Process the authentication or logout request. - * - * @param request - * @param response - * @param context - * @return the status of the flow - * @throws AuthenticationFailedException - * @throws LogoutFailedException - */ - public AuthenticatorFlowStatus process(HttpServletRequest request, - HttpServletResponse response, AuthenticationContext context) - throws AuthenticationFailedException, LogoutFailedException; - - /** - * Get the Context identifier sent with the request. This identifier is used - * to retrieve the state of the authentication/logout flow - * - * @param request - * @return - */ - public String getContextIdentifier(HttpServletRequest request); - - /** - * Get the name of the Authenticator - * @return name - */ - public String getName(); - - /** - * @return - */ - public String getFriendlyName(); - - /** - * Get the claim dialect URI if this authenticator receives claims in a standard dialect - * and needs to be mapped to the Carbon dialect http://wso2.org/claims - * @return boolean - */ - public String getClaimDialectURI(); - - /** - * @return - */ - public List getConfigurationProperties(); - } - ``` - ---- - -## Write the federated authenticator - -To write the federated authenticator: - -1. Create a `maven` project for the custom federated authenticator. Refer the following files: - - The [pom.xml](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.federated.authenticator/pom.xml){:target="_blank"} file used for the sample custom federated authenticator. - - The [service component class](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.federated.authenticator/src/main/java/org/wso2/carbon/identity/custom/federated/authenticator/internal/CustomFederatedAuthenticatorServiceComponent.java){:target="_blank"} file to deploy in WSO2 Identity Server and register it as a federated authenticator. - -2. Write the [custom federated authenticator](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.federated.authenticator/src/main/java/org/wso2/carbon/identity/custom/federated/authenticator/CustomFederatedAuthenticator.java){:target="_blank"} by extending the -[AbstractApplicationAuthenticator](https://github.com/wso2/carbon-identity-framework/blob/v5.18.187/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/AbstractApplicationAuthenticator.java){:target="_blank"} class and implementing the -[FederatedApplicationAuthenticator](https://github.com/wso2/carbon-identity-framework/blob/v5.18.187/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/FederatedApplicationAuthenticator.java){:target="_blank"} class. - -!!! info - See the sample [custom federated authenticator](https://github.com/wso2/samples-is/tree/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.federated.authenticator){:target="_blank"} for your reference. - -??? "Methods in `AbstractApplicationAuthenticator` class and `FederatedApplicationAuthenticator` interface" - | Method | Description | - |-----------|---------------| - |[**public String getName()**](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.federated.authenticator/src/main/java/org/wso2/carbon/identity/custom/federated/authenticator/CustomFederatedAuthenticator.java#L72-L75){:target="_blank"} | Return the name of the authenticator. | - |[**public String getFriendlyName()**](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.federated.authenticator/src/main/java/org/wso2/carbon/identity/custom/federated/authenticator/CustomFederatedAuthenticator.java#L66-L69){:target="_blank"} | Returns the display name for the custom federated authenticator. In this sample, we are using a custom-federated-authenticator. | - | [**public String getContextIdentifier(HttpServletRequest request**)](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.federated.authenticator/src/main/java/org/wso2/carbon/identity/custom/federated/authenticator/CustomFederatedAuthenticator.java#L214-L222){:target="_blank"} | Returns a unique identifier that will map the authentication request and the response. The value returned by the invocation of the authentication request and the response should be the same. | - | [public boolean canHandle(HttpServletRequest request)](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.federated.authenticator/src/main/java/org/wso2/carbon/identity/custom/federated/authenticator/CustomFederatedAuthenticator.java#L59-L63){:target="_blank"}|Specifies whether this authenticator can handle the authentication response. | - | [protected void initiateAuthenticationRequest(HttpServletRequest request,HttpServletResponse response, AuthenticationContext context)](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.federated.authenticator/src/main/java/org/wso2/carbon/identity/custom/federated/authenticator/CustomFederatedAuthenticator.java#L136-L137){:target="_blank"} | Redirects the user to the login page to authenticate and in this sample, the user is redirected to the login page of the application which is configured in the partner identity server which acts as the external service. | - | [protected void processAuthenticationResponse(HttpServletRequest request,HttpServletResponse response, AuthenticationContext context)](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.federated.authenticator/src/main/java/org/wso2/carbon/identity/custom/federated/authenticator/CustomFederatedAuthenticator.java#L168-L169){:target="_blank"} | Implements the logic of the custom federated authenticator. | - ---- - -## Deploy the authenticator - -To deploy the custom federated authenticator: - -1. Open a terminal, navigate to the root of your project, and compile the service by running the following command: - - ``` xml - mvn clean install - ``` - -2. Copy the generated `org.wso2.carbon.identity.custom.federated.authenticator-1.0.0.jar` file from `/target` folder to the `/repository/components/dropins` folder. - ---- - -## Configure the partner identity server - -In this guide, the partner identity server acts as the external system. -Therefore, it is required to run a second WSO2 Identity Server instance as the partner identity server. - -### Set up partner IS -Add the following config to the `/repository/conf/deployment.toml` file to be able to run the partner identity server on the same machine in a different port. - -``` toml -[server] -offset=1 -``` - -After starting that partner identity server, it will run on [localhost:9444](https://localhost:9444/console). - - -### Register an application - -To register and configure the application on the partner IS: - -1. On the WSO2 Identity Server console of the partner identity server, go to **Applications**. - -2. Click **New Application** and select **Traditional Web Application**. - -3. Enter the following details: - - - - - - - - - - - - - - -
NameGive a unique name to identify your application.
ProtocolSelect OpenID Connect.
Authorized redirect URLs - The URL to which the user is redirected after a successful login. Use the following URL for this sample app: -

https://localhost:9443/commonauth

-
- -5. Click **Register**. - -6. Go to the **Protocol** tab and take note of the **Client ID** and the **Client secret**. - -!!! info "Download and configure the application" - On the partner identity server: - - - Download the [sample](https://github.com/asgardeo/asgardeo-tomcat-oidc-agent/releases/latest/download/oidc-sample-app.war){:target="_blank"} application. - - [Configure the sample OIDC]({{base_path}}/get-started/try-samples/qsg-oidc-webapp-java-ee/#configure-the-sample) application on the partner identity server. - -### Add a new user - -To add a new user: - -1. On the WSO2 Identity Server console of the partner identity server, go to **User Management** > **Users**. - -2. Click **Add User** and provide the required details. - -3. Click **Next**. - -4. Add the user to a group, if necessary, and click **Next**. - -5. Assign the user a role, if necessary, and click **Next**. - -6. Review the summary and click **Finish**. - ---- - -## Configure the Identity Server - -This section guides you on how to configure the identity server to use the custom federated authenticator. - -### Prerequisites -- You need to [set up the sample]({{base_path}}/get-started/try-samples/qsg-oidc-webapp-java-ee) application. - -### Configure federated authenticator - -To configure the custom fedrated authenticator: - -1. On the WSO2 Identity Server console, go to **Connections**. -2. Click **Create Connection** and select **Custom Connector**. -3. Provide a unique name and a description and click **Finish**. -4. Go to **Settings** tab and click **New Authenticator**. -5. Select the custom federated authenticator added and click **Next**. -6. Enter the following details: - - | Field name | Value | - |---------------|-------| - | **Client Id** | The Client Id value generated by the external application of the partner identity server. | - | **Client Secret** | The Client Secret value generated by the external application of the partner identity server. | - | **Callback URL** | `https://localhost:9443/commonauth` | - | **Authorization Endpoint URL** | `https://localhost:9444/oauth2/authorize/` | - | **Token Endpoint URL** | `https://localhost:9444/oauth2/token/` | - - ![Federated Authenticator]({{base_path}}/assets/img/references/extend/federated-authenticator.png){: width="800" style="display: block; margin: 0; border: 0.3px solid lightgrey;"} - -5. Click **Finish** to save the configurations. - -### Configure the application with the custom federated authenticator - -To configure the custom federated authenticator for the application: - -1. On the WSO2 Identity Server console, go to **Applications**. - -2. Select your application, go to the **Login Flow** tab and add the custom federated authenticator from your preferred editor: - - --- - === "Classic Editor" - To add custom federated authenticator using the Classic Editor: - - 1. If you haven't already defined a sign-in flow, click **Start with Default configuration** to get started. - - 2. Click **Add Authentication** on the step, select your custom federated authenticator, and click **Add**. - - ![Add custom federated authenticator]({{base_path}}/assets/img/guides/idp/custom-connector/add-custom-federation-with-basic.png){: width="700" style="display: block; margin: 0; border: 0.3px solid lightgrey;"} - - === "Visual Editor" - To add custom federated authenticator using the Visual Editor: - - 1. Switch to the **Visual Editor** tab, by default the `Username & Password` login flow will be added onto the Visual Editor's workspace. - - 2. Click on `+ Add Sign In Option` to add a new authenticator to the same step and select your custom federated authenticator. - - ![Add custom federated authenticator using the Visual Editor]({{base_path}}/assets/img/guides/idp/custom-connector/add-custom-federation-with-visual-editor.png){: width="500" style="display: block; margin: 0; border: 0.3px solid lightgrey;"} - - --- - -3. Click **Update** to save your changes. - ---- - -## Try it out - -1. Access the application URL. - -2. Click **Login**. You will be redirected to the WSO2 Identity Server login page. - -3. Select the sign in option configured. - -4. Enter the credentials of the the new user created in the partner identity server. - -The user is successfully authenticated by the partner Identity Server. - -Similarly, you can write a federated authenticator to authenticate the users using an external system. - -## Contributing your connectors - -{% include "../../../fragments/contributing-connectors.md" %} +{% include "../../../../../../includes/references/extend/federation/write-a-custom-federated-authenticator.md" %} \ No newline at end of file diff --git a/en/identity-server/7.0.0/docs/references/extend/index.md b/en/identity-server/7.0.0/docs/references/extend/index.md index 357db0161f..7707617cc7 100644 --- a/en/identity-server/7.0.0/docs/references/extend/index.md +++ b/en/identity-server/7.0.0/docs/references/extend/index.md @@ -1,11 +1 @@ -# Extensions - -This section discuss how WSO2 Identity Server can be extended related to following aspects. - -- [Authentication]({{base_path}}/references/extend/authentication/oauth2/write-a-custom-oauth-2.0-grant-type) - -- [Identity Federation]({{base_path}}/references/extend/federation/write-a-custom-federated-authenticator) - -- [User Management]({{base_path}}/references/extend/user-mgt/write-a-custom-event-handler) - -- [User Stores]({{base_path}}/references/extend/user-stores/write-a-custom-user-store-manager) \ No newline at end of file +{% include "../../../../../includes/references/extend/index.md" %} \ No newline at end of file diff --git a/en/identity-server/7.0.0/docs/references/extend/user-mgt/provisioning/extend-scim2-user-schemas.md b/en/identity-server/7.0.0/docs/references/extend/user-mgt/provisioning/extend-scim2-user-schemas.md index 904e7b46c3..02b14e3aca 100644 --- a/en/identity-server/7.0.0/docs/references/extend/user-mgt/provisioning/extend-scim2-user-schemas.md +++ b/en/identity-server/7.0.0/docs/references/extend/user-mgt/provisioning/extend-scim2-user-schemas.md @@ -1,215 +1 @@ -# Extend SCIM 2.0 user schemas - -The System for Cross-domain Identity Management (SCIM) is a specification that is designed to manage user identities in cloud-based applications and services in a standardized way to enable interoperability, security, and scalability. It is an emerging open standard which provides RESTful APIs for easier, cheaper, and faster way for creating, provisioning, and maintaining identities. The latest version SCIM 2.0 was released as IETF RFC in September 2015. - -!!! Note - SCIM 2.0 is supported by default in WSO2 Identity Server version 5.4.0 onwards. If you are using WSO2 Identity Server 5.4.0 or a later version, see SCIM 2.0 [Users REST API]({{base_path}}/apis/scim2-users-rest-apis), [Groups REST API]({{base_path}}/apis/scim2-groups-rest-apis), [Server Config REST API]({{base_path}}/apis/scim2-sp-config-rest-apis), [Me REST API]({{base_path}}/apis/scim2-me-rest-apis), and [Roles REST API]({{base_path}}/apis/roles-v2-rest-api/) for instructions on how to use SCIM 2.0 OOTB. - ---- - -## Extend the SCIM 2.0 API - -The SCIM 2.0 (System for Cross-Domain Identity Management) specification defines a [fixed set of default attributes](https://tools.ietf.org/html/rfc7643#section-8.2){:target="_blank"} for the user object. This set is defined to ensure the interoperability and it can cater to most of the industry's identity management requirements. However, in reality, organizations have their own attributes defined for their users which have already been used in their LDAP schemas. Therefore, SCIM is extensible enough to cope with custom attributes and uses the [Enterprise User Extension](https://tools.ietf.org/html/rfc7643#section-8.3){:target="_blank"} to support extra attributes for the SCIM user object. - -WSO2 Identity Server allows users to define their own user attributes in addition to the core user schema and enterprise user schema. These configured schema are then used while creating or validating user objects. This means that custom user attributes can be passed using SCIM for identity management requirements. - -## Extend the SCIM 2.0 API using the Enterprise User Extension - -!!! warning "Not recommended" - From {{product_name}} 6.0.0 onwards, it is recommended to use [SCIM2 Custom User Schema]({{base_path}}/guides/users/attributes/manage-scim2-attribute-mappings) to add custom attributes for users. - - Using enterprise user extension for custom user attributes is not recommended for the following reasons: -
    -
  • According to the current model, enterprise user extension attributes are contained in a file. Hence, any modifications to this file will be applied at the server level.
  • -
  • As Enterprise User Extension is a defined schema, it must remain unchanged.
  • -
- -Follow the steps given below to add a custom attribute using the Enterprise User Extension.  - -1. Open the `scim2-schema-extension.config` file located in the `/repository/conf/` folder. - -2. Define the extension by adding attributes in the following format before the last element of the JSON array in - the `scim2-schema-extension.config` file. - - ``` java - { - "attributeURI":"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:customAttribute", - "attributeName":"customAttribute", - "dataType":"string", - "multiValued":"false", - "description":"customAttribute", - "required":"false", - "caseExact":"false", - "mutability":"readwrite", - "returned":"default", - "uniqueness":"none", - "subAttributes":"null", - "canonicalValues":[], - "referenceTypes":[] - } - ``` - -3. Add the attribute names of the attributes that you added to the `scim2-schema-extension.config` file as `subAttributes` of the `urn:ietf:params:scim:schemas:extension:enterprise:2.0:User` attribute as seen in the code block below. - - ``` java - { - "attributeURI":"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User", - "attributeName":"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User", - "dataType":"complex", - "multiValued":"false", - "description":"Enterprise User", - "required":"false", - "caseExact":"false", - "mutability":"readWrite", - "returned":"default", - "uniqueness":"none", - "subAttributes":"employeeNumber costCenter organization division department manager customAttribute", - "canonicalValues":[], - "referenceTypes":["external"] - } - ``` - -3. Save the file and restart WSO2 Identity Server.  - ---- - -## Add the custom attribute - -1. On the WSO2 Identity Server Console, go to **User Attributes & Stores** > **Attributes**. - -2. Click **Attributes** again under the **Manage Attributes** section. - -3. Click **New Attribute** and enter the following values.  - - **Attribute Name**: customAttribute - - **Attribute Display Name**: Custom Attribute - - !!! tip - Delete the SCIM 2.0 mapping under **Protocol Mappings** since this adds a custom schema. An enterprise schema will be added later in this guide. - - ![add-scim-local-custom-attribute]({{base_path}}/assets/img/references/extend/add-scim-local-custom-attribute.png){: width="600" style="display: block; margin: 0; border: 0.3px solid lightgrey;"} - -4. Click **Finish**.  - -!!! note - - The configuration above is valid when using the PRIMARY userstore. If you have a secondary userstore configured, make sure to add another attribute mapping for the particular user store. - ---- - -## Map the custom attribute - -1. On the WSO2 Identity Server Console, go to **User Attributes & Stores** > **Attributes**. - -2. Under **Manage Attribute Mappings**, click **SCIM 2.0**. - -3. Go to **Enterprise Schema** tab, click **New Attribute** and enter the following values. - - **SCIM Attribute**: customAttribute - - **User Attribute to map to**: Custom Attribute - - ![add-scim-external-attribute]({{base_path}}/assets/img/references/extend/add-scim-external-attribute.png){: width="600" style="display: block; margin: 0; border: 0.3px solid lightgrey;"} - -4. Click **Add Attribute Mapping**. Then click **Save**.  - ---- - -## Try it out - -1. On the WSO2 Identity Server Console, go to **User Management** > **Users**. - -2. Click **Add User**. - -3. Provide the required details and create the user. - -4. In the user profile, add a value for the **Custom Attribute** field and click **Update**. - -5. Retrieve the user using SCIM and ensure that the custom attribute value is present.  - - !!! abstract "" - **SCIM request** - ```bash - curl -v -k --user admin:admin https://localhost:9443/scim2/Users - ``` - --- - **SCIM response** - ``` - { - "totalResults":2, - "startIndex":1, - "itemsPerPage":2, - "schemas":["urn:ietf:params:scim:api:messages:2.0:ListResponse"], - "Resources":[ - { - "emails":["admin@wso2.com"], - "meta":{ - "created":"2020-01-21T16:52:11Z", - "lastModified":"2020-01-21T16:52:11Z" - }, - "roles":[{"type":"default", "value":"Internal/everyone,admin"}], - "name":{"givenName":"admin", "familyName":"admin"}, - "id":"54779523-8677-48bc-bce6-1764b7439f64", - "userName":"admin" - }, - { - "emails":[{"type":"home", "value":"testvalue"},"alex@gmail.com"], - "meta":{ - "created":"2020-01-21T16:56:56Z", - "lastModified":"2020-01-21T17:02:43Z", - "resourceType":"User" - }, - "EnterpriseUser":{"customAttribute":"testvalue"}, - "roles":[{"type":"default", "value":"Internal/everyone"}], - "name":{"givenName":"Alex", "familyName":"alex"}, - "id":"c91b5f1c-08c7-44d3-89c4-34ea9440dea5", - "userName":"alex" - } - ] - } - ``` - -You can also try out updating the custom attribute value using the following SCIM command and checking if the value is updated by accessing the user profile on the WSO2 Identity Server console.  - -!!! abstract "" - **Request format** - ``` - curl -k -X PATCH \ - https://:/scim2/Users/ \ - -H 'Authorization: Basic ' \ - -H 'Content-Type: application/json' \ - -d '{ - "schemas":[ - "urn:ietf:params:scim:api:messages:2.0:PatchOp" - ], - "Operations":[ - { - "op":"replace", - "value":{ - "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": { - "customAttribute": "" - } - } - } - ] - }' - ``` - --- - **Sample request** - ```curl - curl -k -X PATCH \ - https://localhost:9443/scim2/Users/be8f8562-7661-44a9-b6f4-d6d94dac914f \ - -H 'Authorization: Basic dmloYW5nYTphZG1pbg==' \ - -H 'Content-Type: application/json' \ - -d '{ - "schemas":[ - "urn:ietf:params:scim:api:messages:2.0:PatchOp" - ], - "Operations":[ - { - "op":"replace", - "value":{ - "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": { - "customAttribute": "new value" - } - } - } - ] - }' - ``` +{% include "../../../../../../../includes/references/extend/user-mgt/provisioning/extend-scim2-user-schemas.md" %} \ No newline at end of file diff --git a/en/identity-server/7.0.0/docs/references/extend/user-mgt/write-a-custom-event-handler.md b/en/identity-server/7.0.0/docs/references/extend/user-mgt/write-a-custom-event-handler.md index 00df5f7bdd..744a54acb6 100644 --- a/en/identity-server/7.0.0/docs/references/extend/user-mgt/write-a-custom-event-handler.md +++ b/en/identity-server/7.0.0/docs/references/extend/user-mgt/write-a-custom-event-handler.md @@ -1,136 +1 @@ -# Write a custom event handler - -The WSO2 Identity Server eventing framework can be used to trigger events such as user operation events like `PRE_SET_USER_CLAIMS`,`POST_ADD_USER`. A full list of the sample events can be found below. The eventing framework also supports handlers which can be used to do operations upon a triggered event. For instance, an event handler can be used to validate the changed user password against previously used entries when a `PRE_UPDATE_CREDENTIAL` event is triggered. - ---- - -## What is an event handler? - -An event handler is used to perform an operation based on the published events. - -For instance, an event handler can be used to send an email after a user addition. The following sequence of operations are executed while adding a user. - -1. Publish the `PRE_ADD_USER` event — The subscribed handlers will be executed for the pre-add user event. -2. Execute the `AddUser` operation — The user will be persisted to the user store (LDAP or JBDC user store). -3. Publish the `POST_ADD_USER` event — The subscribed handlers will be executed for the post-add user event. - -Therefore, the email can be sent through an event handler that is subscribed to the `POST_ADD_USER` event. - -The following list is a list of sample events. - -- `AUTHENTICATION_STEP_SUCCESS` -- `AUTHENTICATION_STEP_FAILURE` -- `AUTHENTICATION_SUCCESS` -- `AUTHENTICATION_FAILURE` -- `PRE_AUTHENTICATION` -- `POST_AUTHENTICATION` -- `PRE_SET_USER_CLAIMS` -- `POST_SET_USER_CLAIMS` -- `PRE_ADD_USER` -- `POST_ADD_USER` - -!!! info - The other events available with WSO2 Identity Server can be found from the `Event` class [here](https://github.com/wso2/carbon-identity-framework/blob/master/components/identity-event/org.wso2.carbon.identity.event/src/main/java/org/wso2/carbon/identity/event/IdentityEventConstants.java){:target="_blank"}. - - ---- - -## Write an event handler - -To write a new event handler, you must extend the `org.wso2.carbon.identity.event.handler.AbstractEventHandler`. - -1. Override the `getName()` method to set the name for the event handler and the `getPriority()` method can be used to set the priory of the event handler. The handlers will be executed based on the priority. - - ```java - public String getName() { - return "customEventHandler"; - } - - @Override - public int getPriority(MessageContext messageContext) { - return 50; - } - ``` - -2. To execute the expected operation, override the `handleEvent()` method. The `event.getEventProperties()` method can be used to get the parameters related to the user operations. - The `handleEvent()` method should be called from the relevant method, which is written to execute a certain operation and the handlers will be executed once the operation is triggered. - ```java - @Override - public void handleEvent(Event event) throws IdentityEventException { - - Map eventProperties = event.getEventProperties(); - String userName = (String) eventProperties.get(IdentityEventConstants.EventProperty.USER_NAME); - UserStoreManager userStoreManager = (UserStoreManager) eventProperties.get(IdentityEventConstants.EventProperty.USER_STORE_MANAGER); - - String tenantDomain = (String) eventProperties.get(IdentityEventConstants.EventProperty.TENANT_DOMAIN); - String domainName = userStoreManager.getRealmConfiguration().getUserStoreProperty(UserCoreConstants.RealmConfig.PROPERTY_DOMAIN_NAME); - - String[] roleList = (String[]) eventProperties.get(IdentityEventConstants.EventProperty.ROLE_LIST); - ``` - ---- -## Register the event handler - -Register the event handler in the service component as follows. - -```java -protected void activate(ComponentContext context) { - try { - BundleContext bundleContext = context.getBundleContext(); - bundleContext.registerService(AbstractEventHandler. -class.getName(),new SampleEventHandler(), null); - } catch (Exception e) { - ... - } -``` - ---- - -## Configure the event handler - -Add the event handler configuration to the `/repository/conf/deployment.toml` file. The events that need to subscribe to the handler can be listed in subscriptions. - -```toml -[[event_handler]] -name= "customEventHandler" -subscriptions =["CUSTOM_EVENT"] -``` - -## Try out the sample application - -1. Build the sample custom identity event handler [here](https://github.com/wso2/samples-is/tree/v4.6.0/event-handler/custom-identity-event-handler){:target="_blank"} using `mvn clean install` command. -2. Copy the generated org.wso2.carbon.identity.customhandler-4.5.6.jar file in the target folder into `/repository/components/dropins/` folder. -3. Add following configurations to `/repository/conf/deployement.toml` file - ```toml - [[event_handler]] - name="customUserRegistration" - subscriptions=["PRE_ADD_USER","POST_ADD_USER"] - ``` - `name`: The name of the event handler (Name that return from the `getName()` method). - - `subscriptions`: A list of events that the handler will be subscribed to. In this sample application, we are subscribing to the `PRE_ADD_USER` and `POST_ADD_USER` events. - -4. Restart the server. - -!!! note "Note" - In this sample application, the event handler just prints the event properties in the console (Username & tenant-domain). But you can customize the event handler to do whatever you want (e.g., Send an email after adding a user). - -When you want to execute an operation related to an event, publish the event. Then, the handler that is subscribed for the relevant events will be used to execute those events. In the sample configuration given above, the `customUserRegistration` handler is subscribed to the `PRE_ADD_USER, POST_ADD_USER` operations. - - -!!! info - The following sample event handlers are available with WSO2 Identity Server. - - - [UserEmailVerificationHandler](https://github.com/wso2-extensions/identity-governance/blob/master/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/handler/UserEmailVerificationHandler.java){:target="_blank"} - - - [AccountConfirmationValidationHandler](https://github.com/wso2-extensions/identity-governance/blob/master/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/handler/AccountConfirmationValidationHandler.java){:target="_blank"} - - - [AdminForcedPasswordResetHandler](https://github.com/wso2-extensions/identity-governance/blob/master/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/handler/AdminForcedPasswordResetHandler.java){:target="_blank"} - - - [UserSelfRegistrationHandler](https://github.com/wso2-extensions/identity-governance/blob/master/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/handler/UserSelfRegistrationHandler.java){:target="_blank"} - - - [PasswordHistoryValidationHandler](https://github.com/wso2-extensions/identity-governance/blob/master/components/org.wso2.carbon.identity.password.history/src/main/java/org/wso2/carbon/identity/password/history/handler/PasswordHistoryValidationHandler.java){:target="_blank"} - - - [PasswordPolicyValidationHandler](https://github.com/wso2-extensions/identity-governance/blob/master/components/org.wso2.carbon.identity.password.policy/src/main/java/org/wso2/carbon/identity/password/policy/handler/PasswordPolicyValidationHandler.java){:target="_blank"} - - - [AccountSuspensionNotificationHandler](https://github.com/wso2-extensions/identity-governance/blob/master/components/org.wso2.carbon.identity.account.suspension.notification.task/src/main/java/org/wso2/carbon/identity/account/suspension/notification/task/handler/AccountSuspensionNotificationHandler.java){:target="_blank"} +{% include "../../../../../../includes/references/extend/user-mgt/write-a-custom-event-handler.md" %} \ No newline at end of file diff --git a/en/identity-server/7.0.0/docs/references/extend/user-stores/write-a-custom-user-store-manager.md b/en/identity-server/7.0.0/docs/references/extend/user-stores/write-a-custom-user-store-manager.md index 0a8da56372..079ca6be49 100644 --- a/en/identity-server/7.0.0/docs/references/extend/user-stores/write-a-custom-user-store-manager.md +++ b/en/identity-server/7.0.0/docs/references/extend/user-stores/write-a-custom-user-store-manager.md @@ -1,572 +1 @@ -# Write a Custom User Store Manager - -The following sections provide information that you need to be aware of when writing a custom user store manager. - -## Available methods and implementations - -There are a set of methods available in the `AbstractUserStoreManager` class. These methods are used when interacting with user stores. When we implement a custom user store manager, it is important to identify the methods that must be implemented or overridden. - -!!! tip "Overriding methods" - - You must select the methods to override based on your requirement. For example, if you want to change the way you encrypt the password, you only need to implement the `preparePassword` method. If you want to implement a completely new read/write user store manager, you need to implement all the methods listed in the tables given below. - If the user store is read-only, you can implement only the important methods and read methods (if you extend from `AbstractUserStoreManager`, you have to keep the unrelated methods empty). - - There are a few other methods used for internal purposes. You do not need to override those methods. - - -The following list briefly explains the use of the available methods in the `AbstractUserStoreManager` class. Most of the methods provide a configuration option through properties. It is recommended to use these methods with the provided customization options. - -### Important methods - -| Available methods | Default behavior | Reason for overriding | -|-------------------|-------------------|--------------------------| -| `boolean doAuthenticate(String userName, Object credential)` | This method returns details on whether the given username and password are matched or not. The credential is usually a string literal. | If you want to change the authentication logic, you can override this method and write your own implementation. The default task of this method is to compare the given password with the stored password. The given credentials are passed to the `preparePassword` method to do the salting or encryption before the comparison takes place. | -| `String preparePassword(Object password, String saltValue)` | This returns the encrypted or plain-text password based on the configurations. | You can override this method if you need to change the way you encrypt the password. If you want to change the algorithm that is used for encryption, you can configure it. | -| `Properties getDefaultUserStoreProperties()` | The default properties of the user store are returned using this method. These properties are used in user store related operations. Be sure to manually add the following property when you implement the class to control whether the user store is enabled or disabled.: `setOptionalProperty("Disabled", "false", "Whether user store is disabled");` | By overriding this method, you can programmatically change the configuration of the user store manager implementation. | -| `boolean checkUserNameValid(String userName)` | Returns whether the given username is compatible with the defined criteria. | This is the criteria used for defining a valid username that can be configured as a regex in user store configurations. If you want to change the way user name validation is done, override this method. | -| `boolean checkUserPasswordValid(Object credential)` | This returns whether the given password is compatible with the defined criteria. This is invoked when creating a user, updating a password, and authorization. | Similar to the username, you can configure the format of a valid password in configuration. If you want to change that behavior, you can override this method. | - - - -### Write methods - - ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Available methodsDefault behavior

void doAddUser(String userName, Object credential, String[] roleList, Map<String, String> claims, String profileName, boolean requirePasswordChange)

This method is responsible to create a new user based on the given values. We can change the JDBC query or LDAP attribute name with the user store configuration.

void doDeleteUser(String userName)

This removes the user store record related to the given username.

void doUpdateCredential(String userName, Object newCredential, Object oldCredential)

This is responsible to update the credential of the given username after authenticating with the existing password.

void doUpdateCredentialByAdmin(String userName, Object newCredential)

Admin users can use this method to update the credentials of a given user. This can be done without validating the existing password.

void doAddRole(String roleName, String[] userList, boolean shared)

This creates a new user role with the given roleName and maps the given users to the newly created role. The shared parameter indicates whether this role is shared among tenants or not.

void doDeleteRole(String roleName)

This method removes the given role and related mappings from the user store.

void doUpdateRoleName(String roleName, String newRoleName)

This method is used to update the name of the existing roles.

void doUpdateRoleListOfUser(String userName, String[] deletedRoles, String[] newRoles)

This is used to delete the existing mappings between the given user and deletedRoles while creating mappings to newRoles .

void doUpdateUserListOfRole(String roleName, String[] deletedUsers, String[] newUsers)

This is used to delete the existing mappings between the given role and deletedUsers while creating mappings to newUsers .

void doSetUserClaimValue(String userName, String claimURI, String claimValue, String profileName)

This is responsible for creating a new claim for a given user and profile with the given claim URI and value.

void doSetUserClaimValues(String userName, Map<String, String> claims, String profileName)

This is responsible for creating a new claim for a given user and profile with the given list of claim URIs and values.

void doDeleteUserClaimValue(String userName, String claimURI, String profileName)

Removes the existing claim details mapped with the given user and profile

void doDeleteUserClaimValues(String userName, String[] claims, String profileName)

Removes the given list of claims from a given user

void addRememberMe(String userName, String token)

This method is used to persist tokens in the user store.

Group doAddGroup(String groupName, String groupId, List<String> userIds, Map<String, String>claims)

This method is used to create a group with given parameters.

void doAddGroupWithUserIds(String groupName, List<String> userIds)

This method is used to create a group with given user uuid list.

void doUpdateGroupNameByGroupId(String groupId, String newGroupName)

This method is used to update the name of the group with the given group uuid..

void doUpdateUserListOfGroup(String groupId, List<String> deletedUserIds, List<String> newUserIds)

This method is used to update the user list of a group.

void doDeleteGroupByGroupId(String groupId)

This method is used to delete the group with the given id.

void doAddGroupWithUserNames(String groupName, List<String> userNames)

This method is used to create a group with the given name and given set of user names.

void doUpdateGroupName(String currentGroupName, String newGroupName)

This method is used to update name of the group with the given current name.

void doDeleteGroupByGroupName(String groupName)

This method is used to delete the group with the name

- -### Read methods - -| Available methods | Default behavior | -|-------------------|-------------------| -| `boolean doCheckExistingUser(String userName)` | Returns whether the provided `userName` already exists in the user store | -| `boolean doCheckExistingRole(String roleName)` | Returns whether the provided `roleName` already exists in the user store | -| `String[] doListUsers(String filter, int maxItemLimit)` | This method returns a list of usernames that match the given filter string. | -| `String[] doGetRoleNames(String filter, int maxItemLimit)` | Returns a list of role names that match with the given filter string. | -| `String[] doGetExternalRoleListOfUser(String userName, String filter)` | Returns a list of external role names of a given user that match with the given filter string | -| `String[] doGetSharedRoleListOfUser(String userName, String tenantDomain, String filter)` | This method returns a list of shared role names of a given user that match the given filter string. | -| `Map getUserPropertyValues(String userName, String[] propertyNames, String profileName)` | This method returns values for the given `propertyNames` for a given `userName` and `profileName`. | -| `String[] getUserListFromProperties(String property, String value, String profileName)` | This returns a list of usernames that match with the given value of the given property and `profileName`. | -| `String[] doGetDisplayNamesForInternalRole(String[] userNames)` | Returns names to display in the UI for given usernames | -| `Date getPasswordExpirationTime(String userName)` | This returns the password expiry date of a given user. The default value is null. | -| `int getUserId(String username)` | This method returns the identifier of a given username. The default value is 0. | -| `boolean doCheckIsUserInRole(String userName, String roleName)` | `True` is returned if the given user is already mapped to the given role name. | -| `String[] getProfileNames(String userName)` | Returns a list of profile names mapped with a given username. | -| `String[] doGetSharedRoleNames(String tenantDomain, String filter, int maxItemLimit)` | This returns a list of role names that are associated with the given tenant domain and match with the filter. | -| `String[] doGetUserListOfRole(String roleName, String filter)` | This method returns a list of usernames that are mapped with the given rolename. | -| `String[] getAllProfileNames()` | All the profile names are returned including the default profile. | -| `boolean isValidRememberMeToken(String userName, String token)` | This method is used to check if the given token exists for the given user. | -| `boolean isMultipleProfilesAllowed()` | This returns whether this user store is allowed to have multiple profiles per user. The default value is `false`. | -| `boolean isBulkImportSupported()` | This method returns whether this user store allows bulk transactions or not. | -| `String doGetGroupIdFromGroupName(String groupName)` | This method returns group uuid which has the given group name. | -| `String doGetGroupNameFromGroupId(String groupId)` | This method returns group name which has the given group uuid. | -| `Group doGetGroupFromGroupName(String groupName, List requiredAttributes)` | This method returns the group with the given group name. | -| `Group doGetGroupFromGroupId(String groupId, List requiredAttributes)` | This method returns the group with the given group uuid. | -| `List doGetGroupListOfUser(String userId, int limit, int offset, String sortBy, String sortOrder)` | This method returns the group list of a given user. | -| `List doListGroups(Condition condition, int limit, int offset, String sortBy, String sortOrder)` | This method return the group list which matches with the given condition. | - - -### Implementations - -In WSO2 Identity Server, there are four user store manager classes that implement the `AbstractUserStoreManager` class. You can select one of those classes according to the user store that you have in your environment. - - ---- - - - - - - - - - - - - - - - - - - - - - - - - -
userstore manager classWhen you would use it
org.wso2.carbon.user.core.jdbc.UniqueIDJDBCUserStoreManager

If your user details are stored in a database, you must use this user store manager implementation. With the abstraction provided in this implementation, most of the JDBC-based scenarios can be handled without writing a custom user store manager.

org.wso2.carbon.user.core.ldap.UniqueIDReadOnlyLDAPUserStoreManager

You can use this class if you have an LDAP user store. This implementation does not allow you to insert or update users from the WSO2 Identity Server side. Instead, you can only read and use them in the product.

org.wso2.carbon.user.core.ldap.UniqueIDReadWriteLDAPUserStoreManager

If you want to allow WSO2 Identity Server to manipulate user store data, you need to use this implementation.

org.wso2.carbon.user.core.ldap.UniqueIDActiveDirectoryLDAPUserStoreManager

Active Directory also can be used as the user store of WSO2 Identity Server and you can configure it using this user store manager implementation.

- ---- - -## Implement a custom JDBC user store manager - -The instructions in this section are focused on implementing a sample JDBC user store manager. For this sample, the following tools are used to implement the custom user store manager. - -- Java 1.6.0 -- IDE (Eclipse, InteliJ IDEA, etc.) -- Apache Maven - ---- - -## Set up the implementation - -To set up this implementation, follow the instructions given below. - -1. Create a new Apache Maven project with the help of the IDE that you are using. The project should be a simple Apache Maven project and you can use any desired artifact and group ID. -2. Add the WSO2 user store management .jar file as a dependency on our project. Since this .jar file is stored in WSO2's Maven repository, you need to add the WSO2 repository to your POM file. Given below is a sample POM file. - - !!! note - The version number of the carbon dependencies given below has to be updated according to the carbon kernel version that the particular product version is compatible with. - - ??? example "Sample `pom.xml` file" - ``` xml - - - 4.0.0 - org.wso2.sample - CustomReadOnlyJDBCUserStoreManager - 1.0.0 - bundle - - - wso2-nexus - WSO2 internal Repository - http://maven.wso2.org/nexus/content/groups/wso2-public/ - - true - daily - ignore - - - - - - org.wso2.carbon - org.wso2.carbon.user.core - 4.4.11 - - - org.wso2.carbon - org.wso2.carbon.utils - 4.4.11 - - - org.wso2.carbon - org.wso2.carbon.user.api - 4.4.11 - - - - - - - maven-compiler-plugin - 2.3.1 - true - - UTF-8 - 1.7 - 1.7 - - - - org.apache.felix - maven-scr-plugin - 1.7.2 - - - generate-scr-scrdescriptor - - scr - - - - - - org.apache.felix - maven-bundle-plugin - 2.3.5 - true - - - ${project.artifactId} - ${project.artifactId} - - org.wso2.sample.user.store.manager.internal - - - !org.wso2.sample.user.store.manager.internal, - org.wso2.sample.user.store.manager.*, - - - org.wso2.carbon.*, - org.apache.commons.logging.*, - org.osgi.framework.*, - org.osgi.service.component.* - - - - - - - - ``` - - Now your basic implementation is ready. - ---- - -## Write a custom user store manager for a sample scenario - -Consider a sample scenario where you want to use a custom hashing method using a 3rd party library such as [Jasypt](http://www.jasypt.org/) and to do this, you need to override the `doAuthentication` and `preparePassword` methods. - -Follow the steps given below to write a custom user store manager. - -1. Include the required dependencies in your development environment. To do that, include the relevant Apache Maven dependency details or manually add the `.jar` files to your classpath. For example, add the following XML snippet under the dependencies tag in your `pom.xml` file to include the Jasypt dependency. - - ``` xml - - org.jasypt - jasypt - 1.9.2 - - ``` - -2. Create a new class by extending the existing `JDBCUserStoreManager` implementation. The following code is an example of how this would look. - - ??? example "Code sample" - ``` java - package com.wso2.custom.usermgt; - - - package org.wso2.custom.user.store; - - import org.apache.commons.logging.Log; - import org.apache.commons.logging.LogFactory; - import org.jasypt.util.password.StrongPasswordEncryptor; - import org.wso2.carbon.user.api.RealmConfiguration; - import org.wso2.carbon.user.core.UserCoreConstants; - import org.wso2.carbon.user.core.UserRealm; - import org.wso2.carbon.user.core.UserStoreException; - import org.wso2.carbon.user.core.claim.ClaimManager; - import org.wso2.carbon.user.core.common.AuthenticationResult; - import org.wso2.carbon.user.core.common.FailureReason; - import org.wso2.carbon.user.core.common.User; - import org.wso2.carbon.user.core.jdbc.JDBCRealmConstants; - import org.wso2.carbon.user.core.jdbc.UniqueIDJDBCUserStoreManager; - import org.wso2.carbon.user.core.profile.ProfileConfigurationManager; - import org.wso2.carbon.utils.Secret; - - import java.sql.Connection; - import java.sql.PreparedStatement; - import java.sql.ResultSet; - import java.sql.SQLException; - import java.sql.Timestamp; - import java.util.Date; - import java.util.GregorianCalendar; - import java.util.Map; - - /** - * This class implements the Custom User Store Manager. - */ - public class CustomUserStoreManager extends UniqueIDJDBCUserStoreManager { - - private static final Log log = LogFactory.getLog(CustomUserStoreManager.class); - - private static final StrongPasswordEncryptor passwordEncryptor = new StrongPasswordEncryptor(); - - - public CustomUserStoreManager() { - - } - - public CustomUserStoreManager(RealmConfiguration realmConfig, Map properties, ClaimManager - claimManager, ProfileConfigurationManager profileManager, UserRealm realm, Integer tenantId) - throws UserStoreException { - - super(realmConfig, properties, claimManager, profileManager, realm, tenantId); - log.info("CustomUserStoreManager initialized..."); - } - - - @Override - public AuthenticationResult doAuthenticateWithUserName(String userName, Object credential) - throws UserStoreException { - - boolean isAuthenticated = false; - String userID = null; - User user; - // In order to avoid unnecessary db queries. - if (!isValidUserName(userName)) { - String reason = "Username validation failed."; - if (log.isDebugEnabled()) { - log.debug(reason); - } - return getAuthenticationResult(reason); - } - - if (!isValidCredentials(credential)) { - String reason = "Password validation failed."; - if (log.isDebugEnabled()) { - log.debug(reason); - } - return getAuthenticationResult(reason); - } - - try { - String candidatePassword = String.copyValueOf(((Secret) credential).getChars()); - - Connection dbConnection = null; - ResultSet rs = null; - PreparedStatement prepStmt = null; - String sql = null; - dbConnection = this.getDBConnection(); - dbConnection.setAutoCommit(false); - // get the SQL statement used to select user details - sql = this.realmConfig.getUserStoreProperty(JDBCRealmConstants.SELECT_USER_NAME); - if (log.isDebugEnabled()) { - log.debug(sql); - } - - prepStmt = dbConnection.prepareStatement(sql); - prepStmt.setString(1, userName); - // check whether tenant id is used - if (sql.contains(UserCoreConstants.UM_TENANT_COLUMN)) { - prepStmt.setInt(2, this.tenantId); - } - - rs = prepStmt.executeQuery(); - if (rs.next()) { - userID = rs.getString(1); - String storedPassword = rs.getString(3); - - // check whether password is expired or not - boolean requireChange = rs.getBoolean(5); - Timestamp changedTime = rs.getTimestamp(6); - GregorianCalendar gc = new GregorianCalendar(); - gc.add(GregorianCalendar.HOUR, -24); - Date date = gc.getTime(); - if (!(requireChange && changedTime.before(date))) { - // compare the given password with the stored password using jasypt - isAuthenticated = passwordEncryptor.checkPassword(candidatePassword, storedPassword); - } - } - dbConnection.commit(); - log.info(userName + " is authenticated? " + isAuthenticated); - } catch (SQLException exp) { - try { - this.getDBConnection().rollback(); - } catch (SQLException e1) { - throw new UserStoreException("Transaction rollback connection error occurred while" + - " retrieving user authentication info. Authentication Failure.", e1); - } - log.error("Error occurred while retrieving user authentication info.", exp); - throw new UserStoreException("Authentication Failure"); - } - if (isAuthenticated) { - user = getUser(userID, userName); - AuthenticationResult authenticationResult = new AuthenticationResult( - AuthenticationResult.AuthenticationStatus.SUCCESS); - authenticationResult.setAuthenticatedUser(user); - return authenticationResult; - } else { - AuthenticationResult authenticationResult = new AuthenticationResult( - AuthenticationResult.AuthenticationStatus.FAIL); - authenticationResult.setFailureReason(new FailureReason("Invalid credentials.")); - return authenticationResult; - } - } - - @Override - protected String preparePassword(Object password, String saltValue) throws UserStoreException { - if (password != null) { - String candidatePassword = String.copyValueOf(((Secret) password).getChars()); - // ignore saltValue for the time being - log.info("Generating hash value using jasypt..."); - return passwordEncryptor.encryptPassword(candidatePassword); - } else { - log.error("Password cannot be null"); - throw new UserStoreException("Authentication Failure"); - } - } - - private AuthenticationResult getAuthenticationResult(String reason) { - - AuthenticationResult authenticationResult = new AuthenticationResult( - AuthenticationResult.AuthenticationStatus.FAIL); - authenticationResult.setFailureReason(new FailureReason(reason)); - return authenticationResult; - } - } - - - ``` - -!!! note - The default constructor is not enough when you implement - a custom user store manager and you must implement a constructor - with relevant arguments. - ---- - -## Deploy and configure the custom user store manager - -Follow the instructions given below to deploy and configure the custom user store manager. - -1. Copy the artifact of your project (custom-userstore.jar, in this case) to the `/repository/components/dropins` directory. Also copy all the OSGI bundles to this location. If you have any dependency .jar files, copy them to the `/repository/components/lib` directory. - -2. Add the following configuration to the `/repository/conf/deployment.toml` file to use our custom implementation for user store management. - - Although the existing `UniqueID` user stores are allowed by default, when adding a new user store, note that both existing user stores, as well as new user stores, must be configured as shown below. - - !!! abstract "" - **Format** - ```toml - [user_store_mgt] - custom_user_stores=[""] - ``` - --- - **Sample** - ```toml - [user_store_mgt] - custom_user_stores=["com.wso2.custom.usermgt.CustomUserStoreManager"] - ``` - - !!! tip - This step provides instructions on configuring your custom user store manager as a primary user store. Alternatively, you can configure this as a secondary user store if you already have a different primary user store configured. For more information configuring user stores in WSO2 Identity Server, see [Manage user stores]({{base_path}}/guides/users/user-stores). - - - You do not need to change anything else since you extend the - `JDBCUserStoreManager` class, so the configurations will remain the - same. - -You have now implemented a custom user store manager for WSO2 Identity Server. -Once you have done this, start the product and see the log messages that you have placed inside overridden methods when you create a new user or log in. This ensures that all your configurations work as intended. - -!!! note "Do you want to create a custom user store that only has a few enabled methods?" - 1. Sign in to the WSO2 Identity Server management console (`https://:/carbon`). - 2. Click **User Attributes & Stores** > **User stores** > **New User Store**. - 3. Select the custom user store you just created. - 4. Deselect the **Claim Operations Supported** property. - 5. Click **Finish**. - -## Try out the sample user store manager - -1. Build the sample user store manager [here](https://github.com/wso2/samples-is/tree/master/user-mgt/custom-jdbc-user-store-manager){:target="_blank"} using `mvn clean install` command. -2. Copy the generated `org.wso2.custom.user.store-1.0.0.jar` to the `/repository/components/dropins` directory. -3. Add the following configuration to the `/repository/conf/deployment.toml` file to use our custom implementation for user store management. - ```toml - [user_store_mgt] - custom_user_stores=["org.wso2.custom.user.store.CustomUserStoreManager"] - ``` - -4. Restart the Identity Server. - -!!! note "Note" - Once you add the custom user store manager to the Identity server, you have to configure it as a primary or a secondary user store. - \ No newline at end of file +{% include "../../../../../../includes/references/extend/user-stores/write-a-custom-user-store-manager.md" %} \ No newline at end of file diff --git a/en/identity-server/7.0.0/mkdocs.yml b/en/identity-server/7.0.0/mkdocs.yml index f41a87cded..00512da79e 100644 --- a/en/identity-server/7.0.0/mkdocs.yml +++ b/en/identity-server/7.0.0/mkdocs.yml @@ -859,6 +859,7 @@ nav: - Write a custom OAuth2 grant type: references/extend/authentication/oauth2/write-a-custom-oauth-2.0-grant-type.md - Conditional authentication: - Write custom functions for conditional authentication: references/extend/authentication/conditional-auth/write-custom-functions-for-conditional-authentication.md + - Customize the authentication endpoint: references/extend/authentication/customize-the-authentication-endpoint.md - Write a custom local authenticator: references/extend/authentication/write-a-custom-local-authenticator.md - Identity Federation: - Write a custom federated authenticator: references/extend/federation/write-a-custom-federated-authenticator.md diff --git a/en/identity-server/next/docs/assets/img/references/add-access-url-to-app.png b/en/identity-server/next/docs/assets/img/references/add-access-url-to-app.png new file mode 100644 index 0000000000..9ea5f57a17 Binary files /dev/null and b/en/identity-server/next/docs/assets/img/references/add-access-url-to-app.png differ diff --git a/en/identity-server/next/docs/references/extend/authentication/conditional-auth/write-custom-functions-for-conditional-authentication.md b/en/identity-server/next/docs/references/extend/authentication/conditional-auth/write-custom-functions-for-conditional-authentication.md index 33ef852e79..b07c5cf73f 100644 --- a/en/identity-server/next/docs/references/extend/authentication/conditional-auth/write-custom-functions-for-conditional-authentication.md +++ b/en/identity-server/next/docs/references/extend/authentication/conditional-auth/write-custom-functions-for-conditional-authentication.md @@ -1,127 +1 @@ -# Write Custom Functions for Conditional Authentication - -With conditional authentication, it is possible to configure dynamic sequences based on runtime parameters such as the user’s IP address, user role, etc. in the WSO2 Identity Server. This feature allows you to define a dynamic authentication sequence using authentication scripts written in JavaScript. For more information, see [Add conditional authentication]({{base_path}}/guides/authentication/conditional-auth). - -Custom functions that expose any useful WSO2 Identity Server core functions and any utility functions can be added as OSGi services. A core set of functions are available at the following GitHub location: - - -See [Conditional authentication - API reference]({{base_path}}/references/conditional-auth/api-reference) for more information. - ----- - -## Write the function -The following steps provide some guidelines on how you can write custom functions for conditional authentication. - -1. Create an Apache Maven module that has the packaging type as `bundle` and add the `maven-bundle-plugin` configuration. - - !!! info - You may skip this step if you are adding a function to an existing component providing a related functionality. - -2. Create a functional interface (i.e., an Interface with a single public method). The method name and parameters should be the same as the ones you provide from `js`. You may need to use wrapper classes for any object classes (except `number`, `string`, and `boolean`). - - !!! info - - You may skip this step if you can use an existing object. - - For more information on objects, see [object reference documentation]({{base_path}}/references/conditional-auth/api-reference/#object-reference). - - - The JavaScript function is as follows: - - ``` js - var isBar = barMethod(context, "s2", {}); - ``` - - - The functional interface is as follows: - - ``` java - @FunctionalInterface - public interface FooFunction { - - boolean barMethod(JsAuthenticationContext context, String s2, CustomJsObject object); - } - ``` - -3. Create a class that implements the functional interface in the above step and implement your logic. - - ``` java - public class FooFunctionImp implements FooFunction { - boolean barMethod(String s1, String s2, CustomJsObject object) { - //Implementation - } - } - ``` - - !!! note - It is recommended to throw errors from the Java methods to the authentication script. All the errors have to be handled by the method itself. - -4. Add `JsFunctionRegistry` service to the service component class. - - ``` java - @Reference( - service = JsFunctionRegistry.class, - cardinality = ReferenceCardinality.MANDATORY, - policy = ReferencePolicy.DYNAMIC, - unbind = "unsetJsFunctionRegistry" - ) - public void setJsFunctionRegistry(JsFunctionRegistry jsFunctionRegistry) { - FooFunctionsServiceHolder.getInstance().setJsFunctionRegistry(jsFunctionRegistry); - } - - public void unsetJsFunctionRegistry(JsFunctionRegistry jsFunctionRegistry) { - FooFunctionsServiceHolder.getInstance().setJsFunctionRegistry(null); - } - ``` - -5. In the bundle activator of the module, register the class created in step 3 in the `JsFunctionRegistry` service. - - ``` java - @Activate - protected void activate(ComponentContext ctxt) { - - FooFunction fooFunctionImpl = new FooFunctionImpl(); - JsFunctionRegistry jsFunctionRegistry = FooFunctionsServiceHolder.getInstance().getJsFunctionRegistry(); - jsFunctionRegistry.register(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "barMethod", fooFunctionImpl); - } - - @Deactivate - protected void deactivate(ComponentContext ctxt) { - - JsFunctionRegistry jsFunctionRegistry = UserFunctionsServiceHolder.getInstance().getJsFunctionRegistry(); - if (jsFunctionRegistry != null) { - jsFunctionRegistry.deRegister(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "barMethod"); - } - } - ``` - -6. If you have one class that implements many functional interfaces, you need to cast to that particular functional interface when registering. - - ``` java - jsFunctionRegistry.register(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "barMethod", (FooFunction)fooFunctionImpl::barMethod); - ``` - -## Try it out -This section guides you to try out a sample conditional authentication function. - -### Deploy a sample authentication function -1. Build [the sample](https://github.com/wso2/samples-is/tree/v4.6.0/adaptive-authentication/org.wso2.custom.auth.functions){:target="_blank"} using maven `mvn clean install`. -2. Copy the `org.wso2.custom.auth.functions-1.0.0` binary file from `target` directory into `/repository/components/dropins` directory. -3. Restart WSO2 IS. - -### Configure the authentication script -To configure the application with a custom conditional authentication script: - -1. Register an application. -2. Navigate to **Login Flow** tab. -3. Click **Start with default configuration** to define the login flow starting with the `username and password` login. -4. Turn on **Conditional Authentication** by switching the toggle on. You can add the following conditional authentication script using the editor. -5. Click **Update**. - ``` js - var onLoginRequest = function(context) { - executeStep(1, { - onSuccess: function (context) { - var userName = getUsernameFromContext(context, 1); - Log.info("Username: " + userName); - } - }); - }; - ``` - -This custom `getUsernameFromContext()` function can be used to retrieve the username from the authentication context. +{% include "../../../../../../../includes/references/extend/authentication/conditional-auth/write-custom-functions-for-conditional-authentication.md" %} \ No newline at end of file diff --git a/en/identity-server/next/docs/references/extend/authentication/customize-the-authentication-endpoint.md b/en/identity-server/next/docs/references/extend/authentication/customize-the-authentication-endpoint.md new file mode 100644 index 0000000000..3ab6e62d55 --- /dev/null +++ b/en/identity-server/next/docs/references/extend/authentication/customize-the-authentication-endpoint.md @@ -0,0 +1 @@ +{% include "../../../../../../includes/references/extend/authentication/customize-the-authentication-endpoint.md" %} \ No newline at end of file diff --git a/en/identity-server/next/docs/references/extend/authentication/oauth2/write-a-custom-oauth-2.0-grant-type.md b/en/identity-server/next/docs/references/extend/authentication/oauth2/write-a-custom-oauth-2.0-grant-type.md index f56c97918e..7251dde095 100644 --- a/en/identity-server/next/docs/references/extend/authentication/oauth2/write-a-custom-oauth-2.0-grant-type.md +++ b/en/identity-server/next/docs/references/extend/authentication/oauth2/write-a-custom-oauth-2.0-grant-type.md @@ -1,337 +1 @@ -# Write a Custom OAuth 2.0 Grant Type - -OAuth 2.0 authorization servers provide support for four main grant -types according to the [OAuth 2.0 specification](https://tools.ietf.org/html/rfc6749){:target="_blank"}. It -also has the flexibility to support any custom grant types. This topic -provides instructions on how to implement a custom grant type for OAuth -2.0 authorization server and how to extend the behavior of default grant -types. - -The WSO2 Identity Server is used as the OAuth 2.0 authorization server -implementation, which is an open source implementation. - - -## Implement a new grant type - -Follow the steps given below to implement a new grant type. - -1. Implement the following two extensions. - - ` GrantTypeHandler ` - This is the - implementation of the grant type. Here you can implement the - way, it must be validated and how token must be issued. You can - write the new implementation by implementing the - ` AuthorizationGrantHandler ` interface - or by extending - ` AbstractAuthorizationGrantHandler ` . In - most cases, it is enough to extend the - ` AbstractAuthorizationGrantHandler ` in - the WSO2 OAuth component. - - ` GrantTypeValidator ` - This is used to - validate the grant request that is sent to the - ` /token ` endpoint. You can define what - parameters must be in the request and define the validation of - them. You can write the new implementation by extending the - ` AbstractValidator ` in Apache Amber - component. -2. When implementation is done, package your class as a .jar file and - place it in the - ` /repository/component/lib ` directory. -3. To register the custom grant type, configure the - ` /repository/conf/deployment.toml ` - file by adding a new entry, in a manner similar to the following - example; - - ```toml - [[oauth.custom_grant_type]] - name="grant type identifier" - grant_handler="full qualified class name of grant handler" - grant_validator="full qualified class name of grant validator" - [oauth.custom_grant_type.properties] - IdTokenAllowed=true - PublicClientAllowed=true - ``` - - !!! info - Setting the ` ` parameter to - ` true `, provides flexibility to control the - issuing of IDtoken for each grant, and also allows the OIDC scope - validator to validate the grant types that should support the openid - scope. - - Setting the ` ` parameter to - ` true `, provides the capability to configure - whether the grant type can be used by public clients or not. Failing to - set this parameter will result in the grant type being not supported for - public clients. - -4. Restart the server to apply changes. - -To test this out, follow the instructions below to implement a custom-grant type sample. - -## Use the grant type sample - -The sample demonstrated here defines a new sample grant type called the -"mobile" grant type. It is similar to the password grant type and the -only difference is that a mobile number will be passed through instead -of a password. The request to the ` /token ` API must -contain the following two request parameters. - -- ` grant_type=mobile ` -- ` mobileNumber=044322433 ` - -You can access the new grant type project sample from -[here](https://github.com/wso2/samples-is/tree/v4.6.0/oauth2/custom-grant){:target="_blank"} -. The grant handler and validator class is inside the -` org.wso2.sample.identity.oauth2.grant.mobile ` package. -This can be modified as required. - -### Configure the resource - -The following Maven buildable source is compatible with WSO2 Identity -Server 5.1.0 onwards. The attached ` .jar ` file can be -directly used as well. - -| Buildable source | [custom-grant](https://github.com/wso2/samples-is/tree/v4.6.0/oauth2/custom-grant){:target="_blank"} | -|--------------------|------------------------------------------------------------------------------------| -| Built jar file | [custom-grant-4.5.6.jar]({{base_path}}/assets/attachments/custom-grant-4.5.6.jar){:target="_blank"} | - -!!! note - To generate the .jar file from the buildable source, run the following Apache Maven - command in the sample's location using the command line. - - ``` java - mvn clean install - ``` - -1. Copy the .jar file in target directory into the - ` /repository/component/lib ` - directory. -2. Configure the following in the -` /repository/conf/deployment.toml ` - file. - ```toml - [[oauth.custom_grant_type]] - name="mobile" - grant_handler="org.wso2.sample.identity.oauth2.grant.mobile.MobileGrant" - grant_validator="org.wso2.sample.identity.oauth2.grant.mobile.MobileGrantValidator" - [oauth.custom_grant_type.properties] - IdTokenAllowed=true - PublicClientAllowed=true - ``` - -3. Restart the server. - -### Configure an application with the custom grant type - -1. On the WSO2 Identity Server Console, go to **Applications**. - -2. Click **New Application** and select **Standard-Based Application**. - -3. Provide an application name and select **OIDC** as the protocol. - -4. Click **Register** to complete the registration. - -5. In the **Protocol** tab, select the custom grant type (mobile) from the **Allowed Grant Types** list. - -6. Click **Update**. Note the **Client ID** and **Client secret** that appear. - - !!! tip - To configure more advanced configurations, see [OIDC Configurations]({{base_path}}/references/app-settings/oidc-settings-for-app). - -### Obtain an access token - -Send the grant request to the `/token` API using a cURL command. - -!!! note - The HTTP POST body must contain the following two parameters: `grant_type=mobile` and `mobileNumber`. - - ``` - grant_type=mobile&mobileNumber= - ``` - -!!! abstract "" - **Request format** - ``` - curl --user : -k -d "grant_type=&mobileNumber=" -H "Content-Type: application/x-www-form-urlencoded" https://:/oauth2/token - ``` - --- - **Sample request** - ```curl - curl --user 7wYeybBGCVfLxPmS0z66WNMffyMa:WYfwHUsbsEvwtqmDLuaxF_VCQJwa -k -d "grant_type=mobile&mobileNumber=0333444" -H "Content-Type: application/x-www-form-urlencoded" https://localhost:9443/oauth2/token - ``` - -You will receive a response similar to the following JSON response with the access token. - -``` -{ - "access_token": "d9b46507-0300-4a8e-839f-0b856a74c481", - "refresh_token": "45a82bd4-ef55-4368-bc20-9cbc240308d1", - "token_type": "mobile", - "expires_in": 10000 -} -``` - -!!! note "Note" - The access token and the refresh token that are received with the response are just randomly generated by the sample application. Those tokens are not valid tokens. - -## Customize an existing grant type - -As an alternative to creating a custom OAuth grant type, you can -customize one of the existing grant types. The following two classes are -sample implementations of customizing the password grant type in -particular but any other grant type can be customized as well. - -- [RoleBasedPasswordGrant](https://github.com/wso2/samples-is/blob/v4.6.0/oauth2/custom-grant/src/main/java/org/wso2/sample/identity/oauth2/grant/password/RoleBasedPasswordGrant.java){:target="_blank"} - - This does some RBAC validation apart from the authentication before - granting access. -- [ModifiedAccessTokenPasswordGrant](https://github.com/wso2/samples-is/blob/v4.6.0/oauth2/custom-grant/src/main/java/org/wso2/sample/identity/oauth2/grant/password/ModifiedAccessTokenPasswordGrant.java){:target="_blank"} - - This implementation customized the access token value. - -### Configure the resource - -1. Copy the .jar file into the `/repository/component/lib` directory. You can also modify the project and build it using Apache Maven 3. -2. Configure the following in the `/repository/conf/deployment.toml` file. - ```toml - [oauth.grant_type.password] - grant_handler="org.wso2.sample.identity.oauth2.grant.password.ModifiedAccessTokenPasswordGrant" - ``` - -3. Restart the server. - -### Configure an application with the customized password grant type - -1. On the WSO2 Identity Server Console, go to **Applications**. - -2. Click **New Application** and select **Standard-Based Application**. - -3. Provide an application name and select **OIDC** as the protocol. - -4. Click **Register** to complete the registration. - -5. In the **Protocol** tab, select the **Password** grant type from the **Allowed Grant Types** list. - -6. Click **Update**. Note the **Client ID** and **Client secret** that appear. - - !!! tip - To configure more advanced configurations, see [OIDC Configurations]({{base_path}}/references/app-settings/oidc-settings-for-app). - -### Obtain an access token - -Send the password grant request to the `/token` API using a cURL command. - -!!! abstract - **Request format** - ``` - curl --user : -k -d "grant_type=password&username=&password=" -H "Content-Type: application/x-www-form-urlencoded" https://:/oauth2/token - ``` - --- - **Sample request** - ```curl - curl --user 7wYeybBGCVfLxPmS0z66WNMffyMa:WYfwHUsbsEvwtqmDLuaxF_VCQJwa -k -d "grant_type=password&username=admin&password=admin" -H "Content-Type: application/x-www-form-urlencoded" https://localhost:9443/oauth2/token - ``` - -You will receive a response similar to the following JSON response with the access token. - -``` -{ - "token_type":"bearer", - "expires_in":2955, - "refresh_token":"6865c8d67b42c0c23e634a8fc5aa81f", - "access_token":"982f40d4-0bb6-41ce-ac5a-1da06a83e475asela@soasecurity.org" -} -``` - -??? note "Sending Custom Error Codes" - - You can send custom - error codes to the client side in case of defined/identified errors, - using a custom grant handler. - - This simply uses the mobile grant sample and adds the specific error - codes/messages to it. - - !!! tip - See [here](#use-the-grant-type-sample) for instructions on how to implement and use the mobile grant - sample before adding the custom error codes to it. - - In the downloaded Maven [buildable source](https://github.com/wso2/samples-is/tree/v4.6.0/oauth2/custom-grant){:target="_blank"}, the following code segment is added in the sample class - ` org .wso2.sample.identity.oauth2.grant.mobile.MobileGrant ` - inside ` validateGrant() ` method, which is the relevant - code used for this mechanism. - - ``` java - if(mobileNumber != null) { - //validate mobile number - authStatus = isValidMobileNumber(mobileNumber); - - if(authStatus) { - // if valid set authorized mobile number as grant user - AuthenticatedUser mobileUser = new AuthenticatedUser(); - mobileUser.setUserName(mobileNumber); - oAuthTokenReqMessageContext.setAuthorizedUser(mobileUser); - oAuthTokenReqMessageContext.setScope(oAuthTokenReqMessageContext.getOauth2AccessTokenReqDTO().getScope()); - } else{ - ResponseHeader responseHeader = new ResponseHeader(); - responseHeader.setKey("SampleHeader-999"); - responseHeader.setValue("Provided Mobile Number is Invalid."); - oAuthTokenReqMessageContext.addProperty("RESPONSE_HEADERS", new ResponseHeader[]{responseHeader}); - } - - } - ``` - - !!! note - - The `ResponseHeader` code chunk sets a custom response header in case an - invalid mobile number is sent. - - Build the project and copy the modified jar to the `/repository/component/lib` directory. - - **Try it out** - - - Happy Path - - ``` powershell - curl --user : -k -d "grant_type=mobile&mobileNumber=0333444" -H "Content-Type: application/x-www-form-urlencoded" https://:/oauth2/token - ``` - - - Erroneous Path - - ``` powershell - curl -v --user : -k -d "grant_type=mobile&mobileNumber=0363444" -H "Content-Type: application/x-www-form-urlencoded" https://:/oauth2/token - ``` - - !!! info - This is erroneous according to the mobile custom grant sample because - the mobile number does not start with '003'. You can use the -v option - in the cURL command to see the header of the response which should be as - seen in the code block below. - - ``` powershell - > POST /oauth2/token HTTP/1.1 - > Authorization: Basic dlNmZVE5amZOb2RZMXR2OUtMTk54TE93N2t3YTpDRVVXdTdmRE55X1JZZzVsT19tcDhQTGY3blFh - > User-Agent: curl/7.29.0 - > Host: localhost:9443 - > Accept: */* - > Content-Type: application/x-www-form-urlencoded - > Content-Length: 38 - > - * upload completely sent off: 38 out of 38 bytes - < HTTP/1.1 400 Bad Request - < Date: Wed, 13 Jan 2016 06:05:33 GMT - < SampleHeader-999: Provided Mobile Number is Invalid. - < Content-Type: application/json - < Content-Length: 87 - < Connection: close - < Server: WSO2 Carbon Server - < - * Closing connection 0 - * SSLv3, TLS alert, Client hello (1): - {"error":"invalid_grant","error_description":"Provided Authorization Grant is invalid"} - ``` - - !!! info - `SampleHeader-999...` shows the custom header appearing in the headers. - - Similarly this can be used to transfer any custom information to the - client, in a flexible manner. +{% include "../../../../../../../includes/references/extend/authentication/oauth2/write-a-custom-oauth-2.0-grant-type.md" %} \ No newline at end of file diff --git a/en/identity-server/next/docs/references/extend/authentication/write-a-custom-local-authenticator.md b/en/identity-server/next/docs/references/extend/authentication/write-a-custom-local-authenticator.md index 72c08bf991..00e5c07662 100644 --- a/en/identity-server/next/docs/references/extend/authentication/write-a-custom-local-authenticator.md +++ b/en/identity-server/next/docs/references/extend/authentication/write-a-custom-local-authenticator.md @@ -1,146 +1 @@ -# Write a Custom Local Authenticator - -A local authenticator in WSO2 identity server authenticates users, who are stored in the local database, using a defined authentication logic. - -The default local authenticator in WSO2 identity server is the `Username & Password` authenticator. -It authenticates end users stored in a connected user store using the provided username and password. - -WSO2 identity server supports extensibility in local authentication so that you can implement a different authentication logic by writing a custom local authenticator. -You can implement custom authentication logic tailored to your requirement, as follows: - -1. Authenticating users only if they belong to a particular role. -2. Authenticating based on any claim, such as the user's telephone number or employee registration number. -3. Calling another API to authenticate the user. - ---- - -## Scenario - -You have a sample app to which you want users to log in with their telephone numbers instead of usernames. Once a user enters a telephone number, your authentication logic should identify the user and validate the user's credentials. - -The following guide shows you how to write a custom local authenticator to implement this authentication logic. - ---- - -## Write the local authenticator - -To write the local authenticator: - -1. Create a `maven` project for the custom local authenticator. Refer the following files: - - The [pom.xml](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.local.authenticator/pom.xml){:target="_blank"} file used for the sample custom local authenticator. - - The [service component class](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.local.authenticator/src/main/java/org/wso2/carbon/identity/sample/local/authenticator/internal/SampleLocalAuthenticatorServiceComponent.java){:target="_blank"} file to deploy in WSO2 Identity Server and register it as a local authenticator. - -2. Write the [custom local authenticator](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.local.authenticator/src/main/java/org/wso2/carbon/identity/sample/local/authenticator/SampleLocalAuthenticator.java){:target="_blank"} by extending the -[AbstractApplicationAuthenticator](https://github.com/wso2/carbon-identity-framework/blob/v7.0.12/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/AbstractApplicationAuthenticator.java){:target="_blank"} class and implementing the -[LocalApplicationAuthenticator](https://github.com/wso2/carbon-identity-framework/blob/v7.0.12/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/LocalApplicationAuthenticator.java){:target="_blank"} class. - -!!! info - See the sample [custom local authenticator](https://github.com/wso2/samples-is/tree/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.local.authenticator){:target="_blank"} for your reference. - -??? "Methods in `AbstractApplicationAuthenticator` class and `LocalApplicationAuthenticator` interface" - | Method | Description | - |-----------|---------------| - |[**public String getName()**](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.local.authenticator/src/main/java/org/wso2/carbon/identity/sample/local/authenticator/SampleLocalAuthenticator.java#L159){:target="_blank"} | Return the name of the authenticator.

In the sample project, we have used this method to return the name SampleLocalAuthenticator.

| - |[**public String getFriendlyName()**](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.local.authenticator/src/main/java/org/wso2/carbon/identity/sample/local/authenticator/SampleLocalAuthenticator.java#L165){:target="_blank"} | Returns the display name for the custom local authenticator.

In the sample project, we have returned the name sample-local-authenticator.

| - | [**public String getContextIdentifier(HttpServletRequest request**)](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.local.authenticator/src/main/java/org/wso2/carbon/identity/sample/local/authenticator/SampleLocalAuthenticator.java#L153){:target="_blank"} | Returns a unique identifier that will map the authentication request and the response. The value returned by the invocation of the authentication request and the response should be the same. | - | [public boolean canHandle(HttpServletRequest request)](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.local.authenticator/src/main/java/org/wso2/carbon/identity/sample/local/authenticator/SampleLocalAuthenticator.java#L58-L63){:target="_blank"}|

This method checks whether the authentication request is valid, according to the custom authenticator’s requirements. The user will be authenticated if the method returns 'true'. This method also checks whether the authentication or logout request can be handled by the authenticator.

In our sample project, we used this method to check if the username and password are 'not null' in the authentication request. If that succeeds, the authentication flow will continue.

| - | [protected void initiateAuthenticationRequest(HttpServletRequest request,HttpServletResponse response, AuthenticationContext context)](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.local.authenticator/src/main/java/org/wso2/carbon/identity/sample/local/authenticator/SampleLocalAuthenticator.java#L66-L67){:target="_blank"} |

This method is used to redirect the user to the login page to authenticate. You can redirect the user to a custom login URL using this method or you can use the default WSO2 Identity Server login page.

| - | [protected void processAuthenticationResponse(HttpServletRequest request,HttpServletResponse response, AuthenticationContext context)](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.local.authenticator/src/main/java/org/wso2/carbon/identity/sample/local/authenticator/SampleLocalAuthenticator.java#L89-L90){:target="_blank"} |

Implementation of custom authentication logic happens inside this method. For example, you can call any API that can do authentication and authenticate the user or you can authenticate the user against the underlying user store. Then you can also do any other custom logic after authenticating the user such as, you can check if a user belongs to a particular role and allowing authentication accordingly.

In the sample project, we used this method to authenticate the user with the user's telephone number and password, and make the authentication successful.

| - | public AuthenticatorFlowStatus process(HttpServletRequest request, HttpServletResponse response, AuthenticationContext context) |

This method is used to process or carry out the user authentication process. It calls the processAuthenticationResponse() method in the custom authenticator class to execute the custom authentication logic.

In the sample project, we call the super.process(), so that the super class process method will handle the authentication process, instead of implementing our own process method.

- | protected boolean retryAuthenticationEnabled(AuthenticationContext context) |

This method returns a boolean value. If the authentication fails due to some issue like invalid credentials, this method will be called to know whether to retry the authentication flow. If this method returns 'true', the authentication flow will be retried. If returns 'false', the flow is stopped with an exception thrown.

You need to override this method if you are calling the super.process() method. But if you are writing your own process() method, you can handle all the retrying logic accordingly within the method.

| - ---- - -## Deploy the authenticator - -To deploy the custom local authenticator: - -1. Open a terminal, navigate to the root of your project, and compile the service by running the following command: - - ``` xml - mvn clean install - ``` - -2. Copy the generated `org.wso2.carbon.identity.sample.local.authenticator-1.0.0.jar` file from `/target` folder to the `/repository/components/dropins` folder. -3. Add the following config to the deployment.toml file in the `/repository/conf` folder. The name should be the authenticator name returned by the `getName()` method in the authenticator class. - - ``` toml - [authentication.authenticator.sample_authenticator] - name = "SampleLocalAuthenticator" - enable = true - ``` - ---- - -## Configure the Identity Server - -This section guides you on how to configure the identity server to use the custom local authenticator. - -### Prerequisites -- You need to [set up the sample]({{base_path}}/get-started/try-samples/qsg-oidc-webapp-java-ee) application. - -### Configure the application with the custom local authenticator - -To configure the custom local authenticator for the application: - -1. On the WSO2 Identity Server console, go to **Applications**. - -2. Select your application, go to the **Login Flow** tab and add the custom local authenticator from your preferred editor: - - --- - === "Classic Editor" - To add custom local authenticator using the Classic Editor: - - 1. If you haven't already defined a sign-in flow, click **Start with Default configuration** to get started. - - 2. Remove the existing **Username & Password** authenticator and click **Add Authentication** on the step, select your custom local authenticator, and click **Add**. - - ![Add custom federated authenticator]({{base_path}}/assets/img/guides/idp/custom-connector/add-custom-local-authenticator-with-classic-editor.png){: width="700" style="display: block; margin: 0; border: 0.3px solid lightgrey;"} - - === "Visual Editor" - To add custom local authenticator using the Visual Editor: - - 1. Switch to the **Visual Editor** tab, by default the `Username & Password` login flow will be added onto the Visual Editor's workspace. - - 2. Remove the existing **Username & Password** authenticator and click on `+ Add Sign In Option` to add a new authenticator to the same step and select your custom local authenticator. - - ![Add custom local authenticator using the Visual Editor]({{base_path}}/assets/img/guides/idp/custom-connector/add-custom-local-authenticator-with-visual-editor.png){: width="500" style="display: block; margin: 0; border: 0.3px solid lightgrey;"} - - --- - -3. Click **Update** to save your changes. - ---- - -### Add a new user - -To add a new user: - -1. On the WSO2 Identity Server console, go to **User Management** > **Users**. - -2. Click **Add User** and provide the required details. - -3. Add a telephone number to the **Mobile** field and click **Next**. - -5. Add the user to a group, if necessary, and click **Next**. - -6. Assign the user a role, if necessary, and click **Next**. - -7. Review the summary and click **Finish**. - ---- -## Try it out - -1. Access the application URL. - -2. Click **Login**. You will be redirected to the WSO2 Identity Server login page. - -3. Enter the user's **telephone number** as the identifier and enter the user's password, and click **Continue**. - -The user will be successfully authenticated. - ---- - -## Contributing your connectors - -{% include "../../../fragments/contributing-connectors.md" %} +{% include "../../../../../../includes/references/extend/authentication/write-a-custom-local-authenticator.md" %} \ No newline at end of file diff --git a/en/identity-server/next/docs/references/extend/federation/write-a-custom-federated-authenticator.md b/en/identity-server/next/docs/references/extend/federation/write-a-custom-federated-authenticator.md index 3f7361a5fd..deed6d8951 100644 --- a/en/identity-server/next/docs/references/extend/federation/write-a-custom-federated-authenticator.md +++ b/en/identity-server/next/docs/references/extend/federation/write-a-custom-federated-authenticator.md @@ -1,306 +1 @@ -# Write a Custom Federated Authenticator - -A custom federated authenticator can be written to authenticate a user with an external system. -The external system can be any identity provider such as Facebook, Twitter, Google, Yahoo, etc. -You can use the extension points available in WSO2 Identity Server to create custom federated authenticators. - -![Federated authentication diagram]({{base_path}}/assets/img/references/extend/federated-authentication-diagram.png){: width="1000" style="display: block; margin: 0;"} - ---- - -## Authenticator API - -The following is the API used to implement a custom federated -authenticator. - -??? example "Click to view the API of the Application Authenticators" - ``` java - /* - * Copyright (c) 2005-2013, 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.carbon.identity.application.authentication.framework; - - import java.io.Serializable; - import java.util.List; - - import javax.servlet.http.HttpServletRequest; - import javax.servlet.http.HttpServletResponse; - - import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; - import org.wso2.carbon.identity.application.authentication.framework.exception.AuthenticationFailedException; - import org.wso2.carbon.identity.application.authentication.framework.exception.LogoutFailedException; - import org.wso2.carbon.identity.application.common.model.Property; - - /** - * API of the Application Authenticators. - * - */ - public interface ApplicationAuthenticator extends Serializable { - - /** - * Check whether the authentication or logout request can be handled by the - * authenticator - * - * @param request - * @return boolean - */ - public boolean canHandle(HttpServletRequest request); - - /** - * Process the authentication or logout request. - * - * @param request - * @param response - * @param context - * @return the status of the flow - * @throws AuthenticationFailedException - * @throws LogoutFailedException - */ - public AuthenticatorFlowStatus process(HttpServletRequest request, - HttpServletResponse response, AuthenticationContext context) - throws AuthenticationFailedException, LogoutFailedException; - - /** - * Get the Context identifier sent with the request. This identifier is used - * to retrieve the state of the authentication/logout flow - * - * @param request - * @return - */ - public String getContextIdentifier(HttpServletRequest request); - - /** - * Get the name of the Authenticator - * @return name - */ - public String getName(); - - /** - * @return - */ - public String getFriendlyName(); - - /** - * Get the claim dialect URI if this authenticator receives claims in a standard dialect - * and needs to be mapped to the Carbon dialect http://wso2.org/claims - * @return boolean - */ - public String getClaimDialectURI(); - - /** - * @return - */ - public List getConfigurationProperties(); - } - ``` - ---- - -## Write the federated authenticator - -To write the federated authenticator: - -1. Create a `maven` project for the custom federated authenticator. Refer the following files: - - The [pom.xml](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.federated.authenticator/pom.xml){:target="_blank"} file used for the sample custom federated authenticator. - - The [service component class](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.federated.authenticator/src/main/java/org/wso2/carbon/identity/custom/federated/authenticator/internal/CustomFederatedAuthenticatorServiceComponent.java){:target="_blank"} file to deploy in WSO2 Identity Server and register it as a federated authenticator. - -2. Write the [custom federated authenticator](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.federated.authenticator/src/main/java/org/wso2/carbon/identity/custom/federated/authenticator/CustomFederatedAuthenticator.java){:target="_blank"} by extending the -[AbstractApplicationAuthenticator](https://github.com/wso2/carbon-identity-framework/blob/v5.18.187/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/AbstractApplicationAuthenticator.java){:target="_blank"} class and implementing the -[FederatedApplicationAuthenticator](https://github.com/wso2/carbon-identity-framework/blob/v5.18.187/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/FederatedApplicationAuthenticator.java){:target="_blank"} class. - -!!! info - See the sample [custom federated authenticator](https://github.com/wso2/samples-is/tree/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.federated.authenticator){:target="_blank"} for your reference. - -??? "Methods in `AbstractApplicationAuthenticator` class and `FederatedApplicationAuthenticator` interface" - | Method | Description | - |-----------|---------------| - |[**public String getName()**](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.federated.authenticator/src/main/java/org/wso2/carbon/identity/custom/federated/authenticator/CustomFederatedAuthenticator.java#L72-L75){:target="_blank"} | Return the name of the authenticator. | - |[**public String getFriendlyName()**](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.federated.authenticator/src/main/java/org/wso2/carbon/identity/custom/federated/authenticator/CustomFederatedAuthenticator.java#L66-L69){:target="_blank"} | Returns the display name for the custom federated authenticator. In this sample, we are using a custom-federated-authenticator. | - | [**public String getContextIdentifier(HttpServletRequest request**)](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.federated.authenticator/src/main/java/org/wso2/carbon/identity/custom/federated/authenticator/CustomFederatedAuthenticator.java#L214-L222){:target="_blank"} | Returns a unique identifier that will map the authentication request and the response. The value returned by the invocation of the authentication request and the response should be the same. | - | [public boolean canHandle(HttpServletRequest request)](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.federated.authenticator/src/main/java/org/wso2/carbon/identity/custom/federated/authenticator/CustomFederatedAuthenticator.java#L59-L63){:target="_blank"}|Specifies whether this authenticator can handle the authentication response. | - | [protected void initiateAuthenticationRequest(HttpServletRequest request,HttpServletResponse response, AuthenticationContext context)](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.federated.authenticator/src/main/java/org/wso2/carbon/identity/custom/federated/authenticator/CustomFederatedAuthenticator.java#L136-L137){:target="_blank"} | Redirects the user to the login page to authenticate and in this sample, the user is redirected to the login page of the application which is configured in the partner identity server which acts as the external service. | - | [protected void processAuthenticationResponse(HttpServletRequest request,HttpServletResponse response, AuthenticationContext context)](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.federated.authenticator/src/main/java/org/wso2/carbon/identity/custom/federated/authenticator/CustomFederatedAuthenticator.java#L168-L169){:target="_blank"} | Implements the logic of the custom federated authenticator. | - ---- - -## Deploy the authenticator - -To deploy the custom federated authenticator: - -1. Open a terminal, navigate to the root of your project, and compile the service by running the following command: - - ``` xml - mvn clean install - ``` - -2. Copy the generated `org.wso2.carbon.identity.custom.federated.authenticator-1.0.0.jar` file from `/target` folder to the `/repository/components/dropins` folder. - ---- - -## Configure the partner identity server - -In this guide, the partner identity server acts as the external system. -Therefore, it is required to run a second WSO2 Identity Server instance as the partner identity server. - -### Set up partner IS -Add the following config to the `/repository/conf/deployment.toml` file to be able to run the partner identity server on the same machine in a different port. - -``` toml -[server] -offset=1 -``` - -After starting that partner identity server, it will run on [localhost:9444](https://localhost:9444/console). - - -### Register an application - -To register and configure the application on the partner IS: - -1. On the WSO2 Identity Server console of the partner identity server, go to **Applications**. - -2. Click **New Application** and select **Traditional Web Application**. - -3. Enter the following details: - - - - - - - - - - - - - - -
NameGive a unique name to identify your application.
ProtocolSelect OpenID Connect.
Authorized redirect URLs - The URL to which the user is redirected after a successful login. Use the following URL for this sample app: -

https://localhost:9443/commonauth

-
- -5. Click **Register**. - -6. Go to the **Protocol** tab and take note of the **Client ID** and the **Client secret**. - -!!! info "Download and configure the application" - On the partner identity server: - - - Download the [sample](https://github.com/asgardeo/asgardeo-tomcat-oidc-agent/releases/latest/download/oidc-sample-app.war){:target="_blank"} application. - - [Configure the sample OIDC]({{base_path}}/get-started/try-samples/qsg-oidc-webapp-java-ee/#configure-the-sample) application on the partner identity server. - -### Add a new user - -To add a new user: - -1. On the WSO2 Identity Server console of the partner identity server, go to **User Management** > **Users**. - -2. Click **Add User** and provide the required details. - -3. Click **Next**. - -4. Add the user to a group, if necessary, and click **Next**. - -5. Assign the user a role, if necessary, and click **Next**. - -6. Review the summary and click **Finish**. - ---- - -## Configure the Identity Server - -This section guides you on how to configure the identity server to use the custom federated authenticator. - -### Prerequisites -- You need to [set up the sample]({{base_path}}/get-started/try-samples/qsg-oidc-webapp-java-ee) application. - -### Configure federated authenticator - -To configure the custom fedrated authenticator: - -1. On the WSO2 Identity Server console, go to **Connections**. -2. Click **Create Connection** and select **Custom Connector**. -3. Provide a unique name and a description and click **Finish**. -4. Go to **Settings** tab and click **New Authenticator**. -5. Select the custom federated authenticator added and click **Next**. -6. Enter the following details: - - | Field name | Value | - |---------------|-------| - | **Client Id** | The Client Id value generated by the external application of the partner identity server. | - | **Client Secret** | The Client Secret value generated by the external application of the partner identity server. | - | **Callback URL** | `https://localhost:9443/commonauth` | - | **Authorization Endpoint URL** | `https://localhost:9444/oauth2/authorize/` | - | **Token Endpoint URL** | `https://localhost:9444/oauth2/token/` | - - ![Federated Authenticator]({{base_path}}/assets/img/references/extend/federated-authenticator.png){: width="800" style="display: block; margin: 0; border: 0.3px solid lightgrey;"} - -5. Click **Finish** to save the configurations. - -### Configure the application with the custom federated authenticator - -To configure the custom federated authenticator for the application: - -1. On the WSO2 Identity Server console, go to **Applications**. - -2. Select your application, go to the **Login Flow** tab and add the custom federated authenticator from your preferred editor: - - --- - === "Classic Editor" - To add custom federated authenticator using the Classic Editor: - - 1. If you haven't already defined a sign-in flow, click **Start with Default configuration** to get started. - - 2. Click **Add Authentication** on the step, select your custom federated authenticator, and click **Add**. - - ![Add custom federated authenticator]({{base_path}}/assets/img/guides/idp/custom-connector/add-custom-federation-with-basic.png){: width="700" style="display: block; margin: 0; border: 0.3px solid lightgrey;"} - - === "Visual Editor" - To add custom federated authenticator using the Visual Editor: - - 1. Switch to the **Visual Editor** tab, by default the `Username & Password` login flow will be added onto the Visual Editor's workspace. - - 2. Click on `+ Add Sign In Option` to add a new authenticator to the same step and select your custom federated authenticator. - - ![Add custom federated authenticator using the Visual Editor]({{base_path}}/assets/img/guides/idp/custom-connector/add-custom-federation-with-visual-editor.png){: width="500" style="display: block; margin: 0; border: 0.3px solid lightgrey;"} - - --- - -3. Click **Update** to save your changes. - ---- - -## Try it out - -1. Access the application URL. - -2. Click **Login**. You will be redirected to the WSO2 Identity Server login page. - -3. Select the sign in option configured. - -4. Enter the credentials of the the new user created in the partner identity server. - -The user is successfully authenticated by the partner Identity Server. - -Similarly, you can write a federated authenticator to authenticate the users using an external system. - -## Contributing your connectors - -{% include "../../../fragments/contributing-connectors.md" %} +{% include "../../../../../../includes/references/extend/federation/write-a-custom-federated-authenticator.md" %} \ No newline at end of file diff --git a/en/identity-server/next/docs/references/extend/index.md b/en/identity-server/next/docs/references/extend/index.md index 357db0161f..7707617cc7 100644 --- a/en/identity-server/next/docs/references/extend/index.md +++ b/en/identity-server/next/docs/references/extend/index.md @@ -1,11 +1 @@ -# Extensions - -This section discuss how WSO2 Identity Server can be extended related to following aspects. - -- [Authentication]({{base_path}}/references/extend/authentication/oauth2/write-a-custom-oauth-2.0-grant-type) - -- [Identity Federation]({{base_path}}/references/extend/federation/write-a-custom-federated-authenticator) - -- [User Management]({{base_path}}/references/extend/user-mgt/write-a-custom-event-handler) - -- [User Stores]({{base_path}}/references/extend/user-stores/write-a-custom-user-store-manager) \ No newline at end of file +{% include "../../../../../includes/references/extend/index.md" %} \ No newline at end of file diff --git a/en/identity-server/next/docs/references/extend/user-mgt/provisioning/extend-scim2-user-schemas.md b/en/identity-server/next/docs/references/extend/user-mgt/provisioning/extend-scim2-user-schemas.md index 904e7b46c3..02b14e3aca 100644 --- a/en/identity-server/next/docs/references/extend/user-mgt/provisioning/extend-scim2-user-schemas.md +++ b/en/identity-server/next/docs/references/extend/user-mgt/provisioning/extend-scim2-user-schemas.md @@ -1,215 +1 @@ -# Extend SCIM 2.0 user schemas - -The System for Cross-domain Identity Management (SCIM) is a specification that is designed to manage user identities in cloud-based applications and services in a standardized way to enable interoperability, security, and scalability. It is an emerging open standard which provides RESTful APIs for easier, cheaper, and faster way for creating, provisioning, and maintaining identities. The latest version SCIM 2.0 was released as IETF RFC in September 2015. - -!!! Note - SCIM 2.0 is supported by default in WSO2 Identity Server version 5.4.0 onwards. If you are using WSO2 Identity Server 5.4.0 or a later version, see SCIM 2.0 [Users REST API]({{base_path}}/apis/scim2-users-rest-apis), [Groups REST API]({{base_path}}/apis/scim2-groups-rest-apis), [Server Config REST API]({{base_path}}/apis/scim2-sp-config-rest-apis), [Me REST API]({{base_path}}/apis/scim2-me-rest-apis), and [Roles REST API]({{base_path}}/apis/roles-v2-rest-api/) for instructions on how to use SCIM 2.0 OOTB. - ---- - -## Extend the SCIM 2.0 API - -The SCIM 2.0 (System for Cross-Domain Identity Management) specification defines a [fixed set of default attributes](https://tools.ietf.org/html/rfc7643#section-8.2){:target="_blank"} for the user object. This set is defined to ensure the interoperability and it can cater to most of the industry's identity management requirements. However, in reality, organizations have their own attributes defined for their users which have already been used in their LDAP schemas. Therefore, SCIM is extensible enough to cope with custom attributes and uses the [Enterprise User Extension](https://tools.ietf.org/html/rfc7643#section-8.3){:target="_blank"} to support extra attributes for the SCIM user object. - -WSO2 Identity Server allows users to define their own user attributes in addition to the core user schema and enterprise user schema. These configured schema are then used while creating or validating user objects. This means that custom user attributes can be passed using SCIM for identity management requirements. - -## Extend the SCIM 2.0 API using the Enterprise User Extension - -!!! warning "Not recommended" - From {{product_name}} 6.0.0 onwards, it is recommended to use [SCIM2 Custom User Schema]({{base_path}}/guides/users/attributes/manage-scim2-attribute-mappings) to add custom attributes for users. - - Using enterprise user extension for custom user attributes is not recommended for the following reasons: -
    -
  • According to the current model, enterprise user extension attributes are contained in a file. Hence, any modifications to this file will be applied at the server level.
  • -
  • As Enterprise User Extension is a defined schema, it must remain unchanged.
  • -
- -Follow the steps given below to add a custom attribute using the Enterprise User Extension.  - -1. Open the `scim2-schema-extension.config` file located in the `/repository/conf/` folder. - -2. Define the extension by adding attributes in the following format before the last element of the JSON array in - the `scim2-schema-extension.config` file. - - ``` java - { - "attributeURI":"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:customAttribute", - "attributeName":"customAttribute", - "dataType":"string", - "multiValued":"false", - "description":"customAttribute", - "required":"false", - "caseExact":"false", - "mutability":"readwrite", - "returned":"default", - "uniqueness":"none", - "subAttributes":"null", - "canonicalValues":[], - "referenceTypes":[] - } - ``` - -3. Add the attribute names of the attributes that you added to the `scim2-schema-extension.config` file as `subAttributes` of the `urn:ietf:params:scim:schemas:extension:enterprise:2.0:User` attribute as seen in the code block below. - - ``` java - { - "attributeURI":"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User", - "attributeName":"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User", - "dataType":"complex", - "multiValued":"false", - "description":"Enterprise User", - "required":"false", - "caseExact":"false", - "mutability":"readWrite", - "returned":"default", - "uniqueness":"none", - "subAttributes":"employeeNumber costCenter organization division department manager customAttribute", - "canonicalValues":[], - "referenceTypes":["external"] - } - ``` - -3. Save the file and restart WSO2 Identity Server.  - ---- - -## Add the custom attribute - -1. On the WSO2 Identity Server Console, go to **User Attributes & Stores** > **Attributes**. - -2. Click **Attributes** again under the **Manage Attributes** section. - -3. Click **New Attribute** and enter the following values.  - - **Attribute Name**: customAttribute - - **Attribute Display Name**: Custom Attribute - - !!! tip - Delete the SCIM 2.0 mapping under **Protocol Mappings** since this adds a custom schema. An enterprise schema will be added later in this guide. - - ![add-scim-local-custom-attribute]({{base_path}}/assets/img/references/extend/add-scim-local-custom-attribute.png){: width="600" style="display: block; margin: 0; border: 0.3px solid lightgrey;"} - -4. Click **Finish**.  - -!!! note - - The configuration above is valid when using the PRIMARY userstore. If you have a secondary userstore configured, make sure to add another attribute mapping for the particular user store. - ---- - -## Map the custom attribute - -1. On the WSO2 Identity Server Console, go to **User Attributes & Stores** > **Attributes**. - -2. Under **Manage Attribute Mappings**, click **SCIM 2.0**. - -3. Go to **Enterprise Schema** tab, click **New Attribute** and enter the following values. - - **SCIM Attribute**: customAttribute - - **User Attribute to map to**: Custom Attribute - - ![add-scim-external-attribute]({{base_path}}/assets/img/references/extend/add-scim-external-attribute.png){: width="600" style="display: block; margin: 0; border: 0.3px solid lightgrey;"} - -4. Click **Add Attribute Mapping**. Then click **Save**.  - ---- - -## Try it out - -1. On the WSO2 Identity Server Console, go to **User Management** > **Users**. - -2. Click **Add User**. - -3. Provide the required details and create the user. - -4. In the user profile, add a value for the **Custom Attribute** field and click **Update**. - -5. Retrieve the user using SCIM and ensure that the custom attribute value is present.  - - !!! abstract "" - **SCIM request** - ```bash - curl -v -k --user admin:admin https://localhost:9443/scim2/Users - ``` - --- - **SCIM response** - ``` - { - "totalResults":2, - "startIndex":1, - "itemsPerPage":2, - "schemas":["urn:ietf:params:scim:api:messages:2.0:ListResponse"], - "Resources":[ - { - "emails":["admin@wso2.com"], - "meta":{ - "created":"2020-01-21T16:52:11Z", - "lastModified":"2020-01-21T16:52:11Z" - }, - "roles":[{"type":"default", "value":"Internal/everyone,admin"}], - "name":{"givenName":"admin", "familyName":"admin"}, - "id":"54779523-8677-48bc-bce6-1764b7439f64", - "userName":"admin" - }, - { - "emails":[{"type":"home", "value":"testvalue"},"alex@gmail.com"], - "meta":{ - "created":"2020-01-21T16:56:56Z", - "lastModified":"2020-01-21T17:02:43Z", - "resourceType":"User" - }, - "EnterpriseUser":{"customAttribute":"testvalue"}, - "roles":[{"type":"default", "value":"Internal/everyone"}], - "name":{"givenName":"Alex", "familyName":"alex"}, - "id":"c91b5f1c-08c7-44d3-89c4-34ea9440dea5", - "userName":"alex" - } - ] - } - ``` - -You can also try out updating the custom attribute value using the following SCIM command and checking if the value is updated by accessing the user profile on the WSO2 Identity Server console.  - -!!! abstract "" - **Request format** - ``` - curl -k -X PATCH \ - https://:/scim2/Users/ \ - -H 'Authorization: Basic ' \ - -H 'Content-Type: application/json' \ - -d '{ - "schemas":[ - "urn:ietf:params:scim:api:messages:2.0:PatchOp" - ], - "Operations":[ - { - "op":"replace", - "value":{ - "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": { - "customAttribute": "" - } - } - } - ] - }' - ``` - --- - **Sample request** - ```curl - curl -k -X PATCH \ - https://localhost:9443/scim2/Users/be8f8562-7661-44a9-b6f4-d6d94dac914f \ - -H 'Authorization: Basic dmloYW5nYTphZG1pbg==' \ - -H 'Content-Type: application/json' \ - -d '{ - "schemas":[ - "urn:ietf:params:scim:api:messages:2.0:PatchOp" - ], - "Operations":[ - { - "op":"replace", - "value":{ - "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": { - "customAttribute": "new value" - } - } - } - ] - }' - ``` +{% include "../../../../../../../includes/references/extend/user-mgt/provisioning/extend-scim2-user-schemas.md" %} \ No newline at end of file diff --git a/en/identity-server/next/docs/references/extend/user-mgt/write-a-custom-event-handler.md b/en/identity-server/next/docs/references/extend/user-mgt/write-a-custom-event-handler.md index 00df5f7bdd..744a54acb6 100644 --- a/en/identity-server/next/docs/references/extend/user-mgt/write-a-custom-event-handler.md +++ b/en/identity-server/next/docs/references/extend/user-mgt/write-a-custom-event-handler.md @@ -1,136 +1 @@ -# Write a custom event handler - -The WSO2 Identity Server eventing framework can be used to trigger events such as user operation events like `PRE_SET_USER_CLAIMS`,`POST_ADD_USER`. A full list of the sample events can be found below. The eventing framework also supports handlers which can be used to do operations upon a triggered event. For instance, an event handler can be used to validate the changed user password against previously used entries when a `PRE_UPDATE_CREDENTIAL` event is triggered. - ---- - -## What is an event handler? - -An event handler is used to perform an operation based on the published events. - -For instance, an event handler can be used to send an email after a user addition. The following sequence of operations are executed while adding a user. - -1. Publish the `PRE_ADD_USER` event — The subscribed handlers will be executed for the pre-add user event. -2. Execute the `AddUser` operation — The user will be persisted to the user store (LDAP or JBDC user store). -3. Publish the `POST_ADD_USER` event — The subscribed handlers will be executed for the post-add user event. - -Therefore, the email can be sent through an event handler that is subscribed to the `POST_ADD_USER` event. - -The following list is a list of sample events. - -- `AUTHENTICATION_STEP_SUCCESS` -- `AUTHENTICATION_STEP_FAILURE` -- `AUTHENTICATION_SUCCESS` -- `AUTHENTICATION_FAILURE` -- `PRE_AUTHENTICATION` -- `POST_AUTHENTICATION` -- `PRE_SET_USER_CLAIMS` -- `POST_SET_USER_CLAIMS` -- `PRE_ADD_USER` -- `POST_ADD_USER` - -!!! info - The other events available with WSO2 Identity Server can be found from the `Event` class [here](https://github.com/wso2/carbon-identity-framework/blob/master/components/identity-event/org.wso2.carbon.identity.event/src/main/java/org/wso2/carbon/identity/event/IdentityEventConstants.java){:target="_blank"}. - - ---- - -## Write an event handler - -To write a new event handler, you must extend the `org.wso2.carbon.identity.event.handler.AbstractEventHandler`. - -1. Override the `getName()` method to set the name for the event handler and the `getPriority()` method can be used to set the priory of the event handler. The handlers will be executed based on the priority. - - ```java - public String getName() { - return "customEventHandler"; - } - - @Override - public int getPriority(MessageContext messageContext) { - return 50; - } - ``` - -2. To execute the expected operation, override the `handleEvent()` method. The `event.getEventProperties()` method can be used to get the parameters related to the user operations. - The `handleEvent()` method should be called from the relevant method, which is written to execute a certain operation and the handlers will be executed once the operation is triggered. - ```java - @Override - public void handleEvent(Event event) throws IdentityEventException { - - Map eventProperties = event.getEventProperties(); - String userName = (String) eventProperties.get(IdentityEventConstants.EventProperty.USER_NAME); - UserStoreManager userStoreManager = (UserStoreManager) eventProperties.get(IdentityEventConstants.EventProperty.USER_STORE_MANAGER); - - String tenantDomain = (String) eventProperties.get(IdentityEventConstants.EventProperty.TENANT_DOMAIN); - String domainName = userStoreManager.getRealmConfiguration().getUserStoreProperty(UserCoreConstants.RealmConfig.PROPERTY_DOMAIN_NAME); - - String[] roleList = (String[]) eventProperties.get(IdentityEventConstants.EventProperty.ROLE_LIST); - ``` - ---- -## Register the event handler - -Register the event handler in the service component as follows. - -```java -protected void activate(ComponentContext context) { - try { - BundleContext bundleContext = context.getBundleContext(); - bundleContext.registerService(AbstractEventHandler. -class.getName(),new SampleEventHandler(), null); - } catch (Exception e) { - ... - } -``` - ---- - -## Configure the event handler - -Add the event handler configuration to the `/repository/conf/deployment.toml` file. The events that need to subscribe to the handler can be listed in subscriptions. - -```toml -[[event_handler]] -name= "customEventHandler" -subscriptions =["CUSTOM_EVENT"] -``` - -## Try out the sample application - -1. Build the sample custom identity event handler [here](https://github.com/wso2/samples-is/tree/v4.6.0/event-handler/custom-identity-event-handler){:target="_blank"} using `mvn clean install` command. -2. Copy the generated org.wso2.carbon.identity.customhandler-4.5.6.jar file in the target folder into `/repository/components/dropins/` folder. -3. Add following configurations to `/repository/conf/deployement.toml` file - ```toml - [[event_handler]] - name="customUserRegistration" - subscriptions=["PRE_ADD_USER","POST_ADD_USER"] - ``` - `name`: The name of the event handler (Name that return from the `getName()` method). - - `subscriptions`: A list of events that the handler will be subscribed to. In this sample application, we are subscribing to the `PRE_ADD_USER` and `POST_ADD_USER` events. - -4. Restart the server. - -!!! note "Note" - In this sample application, the event handler just prints the event properties in the console (Username & tenant-domain). But you can customize the event handler to do whatever you want (e.g., Send an email after adding a user). - -When you want to execute an operation related to an event, publish the event. Then, the handler that is subscribed for the relevant events will be used to execute those events. In the sample configuration given above, the `customUserRegistration` handler is subscribed to the `PRE_ADD_USER, POST_ADD_USER` operations. - - -!!! info - The following sample event handlers are available with WSO2 Identity Server. - - - [UserEmailVerificationHandler](https://github.com/wso2-extensions/identity-governance/blob/master/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/handler/UserEmailVerificationHandler.java){:target="_blank"} - - - [AccountConfirmationValidationHandler](https://github.com/wso2-extensions/identity-governance/blob/master/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/handler/AccountConfirmationValidationHandler.java){:target="_blank"} - - - [AdminForcedPasswordResetHandler](https://github.com/wso2-extensions/identity-governance/blob/master/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/handler/AdminForcedPasswordResetHandler.java){:target="_blank"} - - - [UserSelfRegistrationHandler](https://github.com/wso2-extensions/identity-governance/blob/master/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/handler/UserSelfRegistrationHandler.java){:target="_blank"} - - - [PasswordHistoryValidationHandler](https://github.com/wso2-extensions/identity-governance/blob/master/components/org.wso2.carbon.identity.password.history/src/main/java/org/wso2/carbon/identity/password/history/handler/PasswordHistoryValidationHandler.java){:target="_blank"} - - - [PasswordPolicyValidationHandler](https://github.com/wso2-extensions/identity-governance/blob/master/components/org.wso2.carbon.identity.password.policy/src/main/java/org/wso2/carbon/identity/password/policy/handler/PasswordPolicyValidationHandler.java){:target="_blank"} - - - [AccountSuspensionNotificationHandler](https://github.com/wso2-extensions/identity-governance/blob/master/components/org.wso2.carbon.identity.account.suspension.notification.task/src/main/java/org/wso2/carbon/identity/account/suspension/notification/task/handler/AccountSuspensionNotificationHandler.java){:target="_blank"} +{% include "../../../../../../includes/references/extend/user-mgt/write-a-custom-event-handler.md" %} \ No newline at end of file diff --git a/en/identity-server/next/docs/references/extend/user-stores/write-a-custom-user-store-manager.md b/en/identity-server/next/docs/references/extend/user-stores/write-a-custom-user-store-manager.md index 0a8da56372..079ca6be49 100644 --- a/en/identity-server/next/docs/references/extend/user-stores/write-a-custom-user-store-manager.md +++ b/en/identity-server/next/docs/references/extend/user-stores/write-a-custom-user-store-manager.md @@ -1,572 +1 @@ -# Write a Custom User Store Manager - -The following sections provide information that you need to be aware of when writing a custom user store manager. - -## Available methods and implementations - -There are a set of methods available in the `AbstractUserStoreManager` class. These methods are used when interacting with user stores. When we implement a custom user store manager, it is important to identify the methods that must be implemented or overridden. - -!!! tip "Overriding methods" - - You must select the methods to override based on your requirement. For example, if you want to change the way you encrypt the password, you only need to implement the `preparePassword` method. If you want to implement a completely new read/write user store manager, you need to implement all the methods listed in the tables given below. - If the user store is read-only, you can implement only the important methods and read methods (if you extend from `AbstractUserStoreManager`, you have to keep the unrelated methods empty). - - There are a few other methods used for internal purposes. You do not need to override those methods. - - -The following list briefly explains the use of the available methods in the `AbstractUserStoreManager` class. Most of the methods provide a configuration option through properties. It is recommended to use these methods with the provided customization options. - -### Important methods - -| Available methods | Default behavior | Reason for overriding | -|-------------------|-------------------|--------------------------| -| `boolean doAuthenticate(String userName, Object credential)` | This method returns details on whether the given username and password are matched or not. The credential is usually a string literal. | If you want to change the authentication logic, you can override this method and write your own implementation. The default task of this method is to compare the given password with the stored password. The given credentials are passed to the `preparePassword` method to do the salting or encryption before the comparison takes place. | -| `String preparePassword(Object password, String saltValue)` | This returns the encrypted or plain-text password based on the configurations. | You can override this method if you need to change the way you encrypt the password. If you want to change the algorithm that is used for encryption, you can configure it. | -| `Properties getDefaultUserStoreProperties()` | The default properties of the user store are returned using this method. These properties are used in user store related operations. Be sure to manually add the following property when you implement the class to control whether the user store is enabled or disabled.: `setOptionalProperty("Disabled", "false", "Whether user store is disabled");` | By overriding this method, you can programmatically change the configuration of the user store manager implementation. | -| `boolean checkUserNameValid(String userName)` | Returns whether the given username is compatible with the defined criteria. | This is the criteria used for defining a valid username that can be configured as a regex in user store configurations. If you want to change the way user name validation is done, override this method. | -| `boolean checkUserPasswordValid(Object credential)` | This returns whether the given password is compatible with the defined criteria. This is invoked when creating a user, updating a password, and authorization. | Similar to the username, you can configure the format of a valid password in configuration. If you want to change that behavior, you can override this method. | - - - -### Write methods - - ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Available methodsDefault behavior

void doAddUser(String userName, Object credential, String[] roleList, Map<String, String> claims, String profileName, boolean requirePasswordChange)

This method is responsible to create a new user based on the given values. We can change the JDBC query or LDAP attribute name with the user store configuration.

void doDeleteUser(String userName)

This removes the user store record related to the given username.

void doUpdateCredential(String userName, Object newCredential, Object oldCredential)

This is responsible to update the credential of the given username after authenticating with the existing password.

void doUpdateCredentialByAdmin(String userName, Object newCredential)

Admin users can use this method to update the credentials of a given user. This can be done without validating the existing password.

void doAddRole(String roleName, String[] userList, boolean shared)

This creates a new user role with the given roleName and maps the given users to the newly created role. The shared parameter indicates whether this role is shared among tenants or not.

void doDeleteRole(String roleName)

This method removes the given role and related mappings from the user store.

void doUpdateRoleName(String roleName, String newRoleName)

This method is used to update the name of the existing roles.

void doUpdateRoleListOfUser(String userName, String[] deletedRoles, String[] newRoles)

This is used to delete the existing mappings between the given user and deletedRoles while creating mappings to newRoles .

void doUpdateUserListOfRole(String roleName, String[] deletedUsers, String[] newUsers)

This is used to delete the existing mappings between the given role and deletedUsers while creating mappings to newUsers .

void doSetUserClaimValue(String userName, String claimURI, String claimValue, String profileName)

This is responsible for creating a new claim for a given user and profile with the given claim URI and value.

void doSetUserClaimValues(String userName, Map<String, String> claims, String profileName)

This is responsible for creating a new claim for a given user and profile with the given list of claim URIs and values.

void doDeleteUserClaimValue(String userName, String claimURI, String profileName)

Removes the existing claim details mapped with the given user and profile

void doDeleteUserClaimValues(String userName, String[] claims, String profileName)

Removes the given list of claims from a given user

void addRememberMe(String userName, String token)

This method is used to persist tokens in the user store.

Group doAddGroup(String groupName, String groupId, List<String> userIds, Map<String, String>claims)

This method is used to create a group with given parameters.

void doAddGroupWithUserIds(String groupName, List<String> userIds)

This method is used to create a group with given user uuid list.

void doUpdateGroupNameByGroupId(String groupId, String newGroupName)

This method is used to update the name of the group with the given group uuid..

void doUpdateUserListOfGroup(String groupId, List<String> deletedUserIds, List<String> newUserIds)

This method is used to update the user list of a group.

void doDeleteGroupByGroupId(String groupId)

This method is used to delete the group with the given id.

void doAddGroupWithUserNames(String groupName, List<String> userNames)

This method is used to create a group with the given name and given set of user names.

void doUpdateGroupName(String currentGroupName, String newGroupName)

This method is used to update name of the group with the given current name.

void doDeleteGroupByGroupName(String groupName)

This method is used to delete the group with the name

- -### Read methods - -| Available methods | Default behavior | -|-------------------|-------------------| -| `boolean doCheckExistingUser(String userName)` | Returns whether the provided `userName` already exists in the user store | -| `boolean doCheckExistingRole(String roleName)` | Returns whether the provided `roleName` already exists in the user store | -| `String[] doListUsers(String filter, int maxItemLimit)` | This method returns a list of usernames that match the given filter string. | -| `String[] doGetRoleNames(String filter, int maxItemLimit)` | Returns a list of role names that match with the given filter string. | -| `String[] doGetExternalRoleListOfUser(String userName, String filter)` | Returns a list of external role names of a given user that match with the given filter string | -| `String[] doGetSharedRoleListOfUser(String userName, String tenantDomain, String filter)` | This method returns a list of shared role names of a given user that match the given filter string. | -| `Map getUserPropertyValues(String userName, String[] propertyNames, String profileName)` | This method returns values for the given `propertyNames` for a given `userName` and `profileName`. | -| `String[] getUserListFromProperties(String property, String value, String profileName)` | This returns a list of usernames that match with the given value of the given property and `profileName`. | -| `String[] doGetDisplayNamesForInternalRole(String[] userNames)` | Returns names to display in the UI for given usernames | -| `Date getPasswordExpirationTime(String userName)` | This returns the password expiry date of a given user. The default value is null. | -| `int getUserId(String username)` | This method returns the identifier of a given username. The default value is 0. | -| `boolean doCheckIsUserInRole(String userName, String roleName)` | `True` is returned if the given user is already mapped to the given role name. | -| `String[] getProfileNames(String userName)` | Returns a list of profile names mapped with a given username. | -| `String[] doGetSharedRoleNames(String tenantDomain, String filter, int maxItemLimit)` | This returns a list of role names that are associated with the given tenant domain and match with the filter. | -| `String[] doGetUserListOfRole(String roleName, String filter)` | This method returns a list of usernames that are mapped with the given rolename. | -| `String[] getAllProfileNames()` | All the profile names are returned including the default profile. | -| `boolean isValidRememberMeToken(String userName, String token)` | This method is used to check if the given token exists for the given user. | -| `boolean isMultipleProfilesAllowed()` | This returns whether this user store is allowed to have multiple profiles per user. The default value is `false`. | -| `boolean isBulkImportSupported()` | This method returns whether this user store allows bulk transactions or not. | -| `String doGetGroupIdFromGroupName(String groupName)` | This method returns group uuid which has the given group name. | -| `String doGetGroupNameFromGroupId(String groupId)` | This method returns group name which has the given group uuid. | -| `Group doGetGroupFromGroupName(String groupName, List requiredAttributes)` | This method returns the group with the given group name. | -| `Group doGetGroupFromGroupId(String groupId, List requiredAttributes)` | This method returns the group with the given group uuid. | -| `List doGetGroupListOfUser(String userId, int limit, int offset, String sortBy, String sortOrder)` | This method returns the group list of a given user. | -| `List doListGroups(Condition condition, int limit, int offset, String sortBy, String sortOrder)` | This method return the group list which matches with the given condition. | - - -### Implementations - -In WSO2 Identity Server, there are four user store manager classes that implement the `AbstractUserStoreManager` class. You can select one of those classes according to the user store that you have in your environment. - - ---- - - - - - - - - - - - - - - - - - - - - - - - - -
userstore manager classWhen you would use it
org.wso2.carbon.user.core.jdbc.UniqueIDJDBCUserStoreManager

If your user details are stored in a database, you must use this user store manager implementation. With the abstraction provided in this implementation, most of the JDBC-based scenarios can be handled without writing a custom user store manager.

org.wso2.carbon.user.core.ldap.UniqueIDReadOnlyLDAPUserStoreManager

You can use this class if you have an LDAP user store. This implementation does not allow you to insert or update users from the WSO2 Identity Server side. Instead, you can only read and use them in the product.

org.wso2.carbon.user.core.ldap.UniqueIDReadWriteLDAPUserStoreManager

If you want to allow WSO2 Identity Server to manipulate user store data, you need to use this implementation.

org.wso2.carbon.user.core.ldap.UniqueIDActiveDirectoryLDAPUserStoreManager

Active Directory also can be used as the user store of WSO2 Identity Server and you can configure it using this user store manager implementation.

- ---- - -## Implement a custom JDBC user store manager - -The instructions in this section are focused on implementing a sample JDBC user store manager. For this sample, the following tools are used to implement the custom user store manager. - -- Java 1.6.0 -- IDE (Eclipse, InteliJ IDEA, etc.) -- Apache Maven - ---- - -## Set up the implementation - -To set up this implementation, follow the instructions given below. - -1. Create a new Apache Maven project with the help of the IDE that you are using. The project should be a simple Apache Maven project and you can use any desired artifact and group ID. -2. Add the WSO2 user store management .jar file as a dependency on our project. Since this .jar file is stored in WSO2's Maven repository, you need to add the WSO2 repository to your POM file. Given below is a sample POM file. - - !!! note - The version number of the carbon dependencies given below has to be updated according to the carbon kernel version that the particular product version is compatible with. - - ??? example "Sample `pom.xml` file" - ``` xml - - - 4.0.0 - org.wso2.sample - CustomReadOnlyJDBCUserStoreManager - 1.0.0 - bundle - - - wso2-nexus - WSO2 internal Repository - http://maven.wso2.org/nexus/content/groups/wso2-public/ - - true - daily - ignore - - - - - - org.wso2.carbon - org.wso2.carbon.user.core - 4.4.11 - - - org.wso2.carbon - org.wso2.carbon.utils - 4.4.11 - - - org.wso2.carbon - org.wso2.carbon.user.api - 4.4.11 - - - - - - - maven-compiler-plugin - 2.3.1 - true - - UTF-8 - 1.7 - 1.7 - - - - org.apache.felix - maven-scr-plugin - 1.7.2 - - - generate-scr-scrdescriptor - - scr - - - - - - org.apache.felix - maven-bundle-plugin - 2.3.5 - true - - - ${project.artifactId} - ${project.artifactId} - - org.wso2.sample.user.store.manager.internal - - - !org.wso2.sample.user.store.manager.internal, - org.wso2.sample.user.store.manager.*, - - - org.wso2.carbon.*, - org.apache.commons.logging.*, - org.osgi.framework.*, - org.osgi.service.component.* - - - - - - - - ``` - - Now your basic implementation is ready. - ---- - -## Write a custom user store manager for a sample scenario - -Consider a sample scenario where you want to use a custom hashing method using a 3rd party library such as [Jasypt](http://www.jasypt.org/) and to do this, you need to override the `doAuthentication` and `preparePassword` methods. - -Follow the steps given below to write a custom user store manager. - -1. Include the required dependencies in your development environment. To do that, include the relevant Apache Maven dependency details or manually add the `.jar` files to your classpath. For example, add the following XML snippet under the dependencies tag in your `pom.xml` file to include the Jasypt dependency. - - ``` xml - - org.jasypt - jasypt - 1.9.2 - - ``` - -2. Create a new class by extending the existing `JDBCUserStoreManager` implementation. The following code is an example of how this would look. - - ??? example "Code sample" - ``` java - package com.wso2.custom.usermgt; - - - package org.wso2.custom.user.store; - - import org.apache.commons.logging.Log; - import org.apache.commons.logging.LogFactory; - import org.jasypt.util.password.StrongPasswordEncryptor; - import org.wso2.carbon.user.api.RealmConfiguration; - import org.wso2.carbon.user.core.UserCoreConstants; - import org.wso2.carbon.user.core.UserRealm; - import org.wso2.carbon.user.core.UserStoreException; - import org.wso2.carbon.user.core.claim.ClaimManager; - import org.wso2.carbon.user.core.common.AuthenticationResult; - import org.wso2.carbon.user.core.common.FailureReason; - import org.wso2.carbon.user.core.common.User; - import org.wso2.carbon.user.core.jdbc.JDBCRealmConstants; - import org.wso2.carbon.user.core.jdbc.UniqueIDJDBCUserStoreManager; - import org.wso2.carbon.user.core.profile.ProfileConfigurationManager; - import org.wso2.carbon.utils.Secret; - - import java.sql.Connection; - import java.sql.PreparedStatement; - import java.sql.ResultSet; - import java.sql.SQLException; - import java.sql.Timestamp; - import java.util.Date; - import java.util.GregorianCalendar; - import java.util.Map; - - /** - * This class implements the Custom User Store Manager. - */ - public class CustomUserStoreManager extends UniqueIDJDBCUserStoreManager { - - private static final Log log = LogFactory.getLog(CustomUserStoreManager.class); - - private static final StrongPasswordEncryptor passwordEncryptor = new StrongPasswordEncryptor(); - - - public CustomUserStoreManager() { - - } - - public CustomUserStoreManager(RealmConfiguration realmConfig, Map properties, ClaimManager - claimManager, ProfileConfigurationManager profileManager, UserRealm realm, Integer tenantId) - throws UserStoreException { - - super(realmConfig, properties, claimManager, profileManager, realm, tenantId); - log.info("CustomUserStoreManager initialized..."); - } - - - @Override - public AuthenticationResult doAuthenticateWithUserName(String userName, Object credential) - throws UserStoreException { - - boolean isAuthenticated = false; - String userID = null; - User user; - // In order to avoid unnecessary db queries. - if (!isValidUserName(userName)) { - String reason = "Username validation failed."; - if (log.isDebugEnabled()) { - log.debug(reason); - } - return getAuthenticationResult(reason); - } - - if (!isValidCredentials(credential)) { - String reason = "Password validation failed."; - if (log.isDebugEnabled()) { - log.debug(reason); - } - return getAuthenticationResult(reason); - } - - try { - String candidatePassword = String.copyValueOf(((Secret) credential).getChars()); - - Connection dbConnection = null; - ResultSet rs = null; - PreparedStatement prepStmt = null; - String sql = null; - dbConnection = this.getDBConnection(); - dbConnection.setAutoCommit(false); - // get the SQL statement used to select user details - sql = this.realmConfig.getUserStoreProperty(JDBCRealmConstants.SELECT_USER_NAME); - if (log.isDebugEnabled()) { - log.debug(sql); - } - - prepStmt = dbConnection.prepareStatement(sql); - prepStmt.setString(1, userName); - // check whether tenant id is used - if (sql.contains(UserCoreConstants.UM_TENANT_COLUMN)) { - prepStmt.setInt(2, this.tenantId); - } - - rs = prepStmt.executeQuery(); - if (rs.next()) { - userID = rs.getString(1); - String storedPassword = rs.getString(3); - - // check whether password is expired or not - boolean requireChange = rs.getBoolean(5); - Timestamp changedTime = rs.getTimestamp(6); - GregorianCalendar gc = new GregorianCalendar(); - gc.add(GregorianCalendar.HOUR, -24); - Date date = gc.getTime(); - if (!(requireChange && changedTime.before(date))) { - // compare the given password with the stored password using jasypt - isAuthenticated = passwordEncryptor.checkPassword(candidatePassword, storedPassword); - } - } - dbConnection.commit(); - log.info(userName + " is authenticated? " + isAuthenticated); - } catch (SQLException exp) { - try { - this.getDBConnection().rollback(); - } catch (SQLException e1) { - throw new UserStoreException("Transaction rollback connection error occurred while" + - " retrieving user authentication info. Authentication Failure.", e1); - } - log.error("Error occurred while retrieving user authentication info.", exp); - throw new UserStoreException("Authentication Failure"); - } - if (isAuthenticated) { - user = getUser(userID, userName); - AuthenticationResult authenticationResult = new AuthenticationResult( - AuthenticationResult.AuthenticationStatus.SUCCESS); - authenticationResult.setAuthenticatedUser(user); - return authenticationResult; - } else { - AuthenticationResult authenticationResult = new AuthenticationResult( - AuthenticationResult.AuthenticationStatus.FAIL); - authenticationResult.setFailureReason(new FailureReason("Invalid credentials.")); - return authenticationResult; - } - } - - @Override - protected String preparePassword(Object password, String saltValue) throws UserStoreException { - if (password != null) { - String candidatePassword = String.copyValueOf(((Secret) password).getChars()); - // ignore saltValue for the time being - log.info("Generating hash value using jasypt..."); - return passwordEncryptor.encryptPassword(candidatePassword); - } else { - log.error("Password cannot be null"); - throw new UserStoreException("Authentication Failure"); - } - } - - private AuthenticationResult getAuthenticationResult(String reason) { - - AuthenticationResult authenticationResult = new AuthenticationResult( - AuthenticationResult.AuthenticationStatus.FAIL); - authenticationResult.setFailureReason(new FailureReason(reason)); - return authenticationResult; - } - } - - - ``` - -!!! note - The default constructor is not enough when you implement - a custom user store manager and you must implement a constructor - with relevant arguments. - ---- - -## Deploy and configure the custom user store manager - -Follow the instructions given below to deploy and configure the custom user store manager. - -1. Copy the artifact of your project (custom-userstore.jar, in this case) to the `/repository/components/dropins` directory. Also copy all the OSGI bundles to this location. If you have any dependency .jar files, copy them to the `/repository/components/lib` directory. - -2. Add the following configuration to the `/repository/conf/deployment.toml` file to use our custom implementation for user store management. - - Although the existing `UniqueID` user stores are allowed by default, when adding a new user store, note that both existing user stores, as well as new user stores, must be configured as shown below. - - !!! abstract "" - **Format** - ```toml - [user_store_mgt] - custom_user_stores=[""] - ``` - --- - **Sample** - ```toml - [user_store_mgt] - custom_user_stores=["com.wso2.custom.usermgt.CustomUserStoreManager"] - ``` - - !!! tip - This step provides instructions on configuring your custom user store manager as a primary user store. Alternatively, you can configure this as a secondary user store if you already have a different primary user store configured. For more information configuring user stores in WSO2 Identity Server, see [Manage user stores]({{base_path}}/guides/users/user-stores). - - - You do not need to change anything else since you extend the - `JDBCUserStoreManager` class, so the configurations will remain the - same. - -You have now implemented a custom user store manager for WSO2 Identity Server. -Once you have done this, start the product and see the log messages that you have placed inside overridden methods when you create a new user or log in. This ensures that all your configurations work as intended. - -!!! note "Do you want to create a custom user store that only has a few enabled methods?" - 1. Sign in to the WSO2 Identity Server management console (`https://:/carbon`). - 2. Click **User Attributes & Stores** > **User stores** > **New User Store**. - 3. Select the custom user store you just created. - 4. Deselect the **Claim Operations Supported** property. - 5. Click **Finish**. - -## Try out the sample user store manager - -1. Build the sample user store manager [here](https://github.com/wso2/samples-is/tree/master/user-mgt/custom-jdbc-user-store-manager){:target="_blank"} using `mvn clean install` command. -2. Copy the generated `org.wso2.custom.user.store-1.0.0.jar` to the `/repository/components/dropins` directory. -3. Add the following configuration to the `/repository/conf/deployment.toml` file to use our custom implementation for user store management. - ```toml - [user_store_mgt] - custom_user_stores=["org.wso2.custom.user.store.CustomUserStoreManager"] - ``` - -4. Restart the Identity Server. - -!!! note "Note" - Once you add the custom user store manager to the Identity server, you have to configure it as a primary or a secondary user store. - \ No newline at end of file +{% include "../../../../../../includes/references/extend/user-stores/write-a-custom-user-store-manager.md" %} \ No newline at end of file diff --git a/en/includes/guides/fragments/contributing-connectors.md b/en/includes/guides/fragments/contributing-connectors.md new file mode 100644 index 0000000000..d22d8a0bb0 --- /dev/null +++ b/en/includes/guides/fragments/contributing-connectors.md @@ -0,0 +1,15 @@ + +You are welcome to share and host your connectors in our [connector store](https://store.wso2.com/store/assets/isconnector/list){:target="_blank"}. As an organization that believes in the open source paradigm, we always encourage our community to make contributions so that our community around WSO2 Identity server can benefit from using an open source product. +Contributions can be made by following the procedure below. + +1. Create an issue in the [product-is git repository](https://github.com/wso2/product-is/issues/new?assignees=&labels=feature&template=feature_request.md&title=){:target="_blank"}. +2. Provide a basic description of the purpose and functionality of your connector. Include the following information in the git issue. + - Downloadable connector artifact (ex: JAR files). + - Documentation on setup and testing instructions for the connector. + - Source code repository of the connector. +3. Make sure your source code follows WSO2 coding standards and [secure development guidelines](https://wso2.com/technical-reports/wso2-secure-engineering-guidelines/){:target="_blank"}. +4. Our team will perform necessary functionality, security, and usability testing on the connector and provide suggestions for improvements. +5. If your code and the functionality are deemed to comply with the standards of WSO2, you will be able to get the connector hosted on our connector store and contribute the source code to WSO2. +6. You will be required to sign the [Contributor License Agreement](https://wso2.com/files/wso2-cla.pdf){:target="_blank"} once the connector is accepted. + +You can also contact us through our [Discord channel](https://discord.com/invite/wso2){:target="_blank"} if you need more information. diff --git a/en/includes/references/extend/authentication/conditional-auth/write-custom-functions-for-conditional-authentication.md b/en/includes/references/extend/authentication/conditional-auth/write-custom-functions-for-conditional-authentication.md new file mode 100644 index 0000000000..33ef852e79 --- /dev/null +++ b/en/includes/references/extend/authentication/conditional-auth/write-custom-functions-for-conditional-authentication.md @@ -0,0 +1,127 @@ +# Write Custom Functions for Conditional Authentication + +With conditional authentication, it is possible to configure dynamic sequences based on runtime parameters such as the user’s IP address, user role, etc. in the WSO2 Identity Server. This feature allows you to define a dynamic authentication sequence using authentication scripts written in JavaScript. For more information, see [Add conditional authentication]({{base_path}}/guides/authentication/conditional-auth). + +Custom functions that expose any useful WSO2 Identity Server core functions and any utility functions can be added as OSGi services. A core set of functions are available at the following GitHub location: + + +See [Conditional authentication - API reference]({{base_path}}/references/conditional-auth/api-reference) for more information. + +---- + +## Write the function +The following steps provide some guidelines on how you can write custom functions for conditional authentication. + +1. Create an Apache Maven module that has the packaging type as `bundle` and add the `maven-bundle-plugin` configuration. + + !!! info + You may skip this step if you are adding a function to an existing component providing a related functionality. + +2. Create a functional interface (i.e., an Interface with a single public method). The method name and parameters should be the same as the ones you provide from `js`. You may need to use wrapper classes for any object classes (except `number`, `string`, and `boolean`). + + !!! info + - You may skip this step if you can use an existing object. + - For more information on objects, see [object reference documentation]({{base_path}}/references/conditional-auth/api-reference/#object-reference). + + - The JavaScript function is as follows: + + ``` js + var isBar = barMethod(context, "s2", {}); + ``` + + - The functional interface is as follows: + + ``` java + @FunctionalInterface + public interface FooFunction { + + boolean barMethod(JsAuthenticationContext context, String s2, CustomJsObject object); + } + ``` + +3. Create a class that implements the functional interface in the above step and implement your logic. + + ``` java + public class FooFunctionImp implements FooFunction { + boolean barMethod(String s1, String s2, CustomJsObject object) { + //Implementation + } + } + ``` + + !!! note + It is recommended to throw errors from the Java methods to the authentication script. All the errors have to be handled by the method itself. + +4. Add `JsFunctionRegistry` service to the service component class. + + ``` java + @Reference( + service = JsFunctionRegistry.class, + cardinality = ReferenceCardinality.MANDATORY, + policy = ReferencePolicy.DYNAMIC, + unbind = "unsetJsFunctionRegistry" + ) + public void setJsFunctionRegistry(JsFunctionRegistry jsFunctionRegistry) { + FooFunctionsServiceHolder.getInstance().setJsFunctionRegistry(jsFunctionRegistry); + } + + public void unsetJsFunctionRegistry(JsFunctionRegistry jsFunctionRegistry) { + FooFunctionsServiceHolder.getInstance().setJsFunctionRegistry(null); + } + ``` + +5. In the bundle activator of the module, register the class created in step 3 in the `JsFunctionRegistry` service. + + ``` java + @Activate + protected void activate(ComponentContext ctxt) { + + FooFunction fooFunctionImpl = new FooFunctionImpl(); + JsFunctionRegistry jsFunctionRegistry = FooFunctionsServiceHolder.getInstance().getJsFunctionRegistry(); + jsFunctionRegistry.register(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "barMethod", fooFunctionImpl); + } + + @Deactivate + protected void deactivate(ComponentContext ctxt) { + + JsFunctionRegistry jsFunctionRegistry = UserFunctionsServiceHolder.getInstance().getJsFunctionRegistry(); + if (jsFunctionRegistry != null) { + jsFunctionRegistry.deRegister(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "barMethod"); + } + } + ``` + +6. If you have one class that implements many functional interfaces, you need to cast to that particular functional interface when registering. + + ``` java + jsFunctionRegistry.register(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "barMethod", (FooFunction)fooFunctionImpl::barMethod); + ``` + +## Try it out +This section guides you to try out a sample conditional authentication function. + +### Deploy a sample authentication function +1. Build [the sample](https://github.com/wso2/samples-is/tree/v4.6.0/adaptive-authentication/org.wso2.custom.auth.functions){:target="_blank"} using maven `mvn clean install`. +2. Copy the `org.wso2.custom.auth.functions-1.0.0` binary file from `target` directory into `/repository/components/dropins` directory. +3. Restart WSO2 IS. + +### Configure the authentication script +To configure the application with a custom conditional authentication script: + +1. Register an application. +2. Navigate to **Login Flow** tab. +3. Click **Start with default configuration** to define the login flow starting with the `username and password` login. +4. Turn on **Conditional Authentication** by switching the toggle on. You can add the following conditional authentication script using the editor. +5. Click **Update**. + ``` js + var onLoginRequest = function(context) { + executeStep(1, { + onSuccess: function (context) { + var userName = getUsernameFromContext(context, 1); + Log.info("Username: " + userName); + } + }); + }; + ``` + +This custom `getUsernameFromContext()` function can be used to retrieve the username from the authentication context. diff --git a/en/includes/references/extend/authentication/customize-the-authentication-endpoint.md b/en/includes/references/extend/authentication/customize-the-authentication-endpoint.md new file mode 100644 index 0000000000..abf4494231 --- /dev/null +++ b/en/includes/references/extend/authentication/customize-the-authentication-endpoint.md @@ -0,0 +1,249 @@ +# Customize the Authentication Endpoint + +The authentication endpoint is the URL used in authentication requests. The following sections discuss methods of customizing this endpoint for various scenarios. + +## Customize the authentication endpoint URL + +The authentication endpoint URL is the location in your web application that contains authentication related pages. To customize this endpoint, + +1. Add the following configuration to the `deployment.toml` file found in the `/repository/conf/` directory and change the value of the `login_url` parameter depending on the URL on which the web application should run. + + ```toml + [authentication.endpoints] + login_url="/sso/login" + ``` + +2. Run the web application on the new authentication endpoint URL. + +## Customize the request parameters + +In addition to the authentication URL itself, you may customize the request parameters sent with the authentication URL by adding the following configuration to the `deployment.toml` file found in the `/repository/conf/` directory. + +```toml +[authentication.endpoint.query_params] +filter_policy = exclude +filter_parameters = [username] +filter_parameters = [password] +``` + +!!! note + In the above configuration, username and password are simply examples. You can add any query parameter of your choosing and here for your request and customize it according to your specifications. + +--- + +## Enable multitenancy for the login page + +If your {{product_name}} setup includes multiple tenants, users typically log in by entering their username in the format `username@domain-name`. However, you can simplify this process by displaying a dropdown menu on the login page that lists all available tenants. Users can then select their tenant from the dropdown and enter their username without needing to specify the domain. To enable this feature: + +1. Add the following configurations to the `deployment.toml` file found in the `/repository/conf/` directory. + + - This configuration disables the certificate authentication on certain occasions (e.g. + when working on mobile apps). This makes two-way SSL authentication optional. + + ``` toml + [transport.https.properties] + clientAuth="want" + ``` + + - The following configuration enables the Mutual SSL Authenticator. + + ```toml + [admin_console.authenticator.mutual_ssl_authenticator] + enable = true + ``` + + - If the `saml_sso_authenticator` is enabled as shown below, set its priority to 0. Otherwise ignore this step. + + ```toml + [admin_console.authenticator.saml_sso_authenticator] + enable = true + priority = 0 + ``` + + - Add the following configuration to enable multitenancy. + + ```toml + [tenant] + data_listener_urls = ["url1","url2"] + [tenant.domain_drop_down] + enable = true + ``` + + !!! note + + When configuring the `data_listener_urls` property in the above configuration, note the + following. + + - In a clustered setup that has multiple authentication endpoint web applications hosted, list all of them under the `data_listener_urls` property. + + - For authentication endpoint web applications hosted outside the WSO2 Identity Server or in other nodes of a cluster, add the absolute URL within the `data_listener_urls` property. + +2. Restart the server using one of the following commands. + + - **Windows** : `wso2server.bat` + - **Linux/Unix** : `sh wso2server.sh` + +3. Once the server is restarted, the **authenticationendpoint.war** file is deployed. The required properties should be configured in the `/repository/conf/deployment.toml`. The following are the default values for the properties to be configured. + + ``` toml + [identity.auth_framework.endpoint] + tenant_list_enabled="false" + hostname_verification_enabled="true" + mutual_ssl_username="admin" + client_keystore="wso2carbon.jks" + carbon_security_keystore_password="wso2carbon" + client_truststore="client-truststore.jks" + carbon_security_truststore_password="wso2carbon" + identity_server_service_url="https://localhost:9443" + username_header="UserName" + ``` + + Make the following updates: + + - Set `tenant_list_enabled` to `true` in order for the tenants to be displayed as a list. + + - For the `mutual_ssl_username` property, set a username to be used for mutual SSL authentication. This user needs to have permission to list down tenants. + + - Paths for client keystore and truststore can be relative paths or absolute paths. The default paths point to the keystore and truststore of the Identity Server itself. A new keystore can be created and used for the client if necessary. However, you must set the passwords for `carbon_security_keystore_password` and `carbon_security_truststore_password` appropriately. + + !!! note + If you are hosting the `autheticationendpoint.war` webapp outside the Identity Server (i.e in a different Tomcat or WSO2 Application Server), you cannot add these configurations to the `/repository/conf/deployment.toml` file as the webapp does not have access to it. Instead, the same property file can be found at `/authenticationendpoint/WEB-INF/classes/EndpointConfig.properties`. + + The following are the default values for the properties to be used in this file. + + ``` + tenantListEnabled=false + mutualSSLManagerEnabled=true + hostname.verification.enabled=true + mutual.ssl.username=admin + client.keyStore=./repository/resources/security/wso2carbon.jks + Carbon.Security.KeyStore.Password=wso2carbon + client.trustStore=./repository/resources/security/client-truststore.jks + Carbon.Security.TrustStore.Password=wso2carbon + identity.server.serviceURL=https://localhost:9443/services/ + username.header=UserName + key.manager.type=SunX509 + trust.manager.type=SunX509 + tls.protocol=TLSv1.2 + app.name=dashboard + app.password=dashboard + identity.server.origin=${carbon.protocol}://${carbon.host}:${carbon.management.port} + ``` + + In this scenario, do the following: + + - Provide the full URL of the {{product_name}} admin services endpoint to the `identity.server.serviceURL` property in the format `identity.server.serviceURL=https://:/services`. + + - Set `tenant_list_enabled` to `true` in order for the tenants to be displayed as a list. + - For the `mutual_ssl_username` property, set a username to be used for mutual SSL authentication. This user needs to have permission to list down tenants. + + - Paths for client keystore and truststore can be relative paths or absolute paths. The default paths point to the keystore and truststore of the Identity Server itself. A new keystore can be created and used for the client if necessary. However, you must set the passwords for `carbon_security_keystore_password` and `carbon_security_truststore_password` appropriately. + +4. For mutual SSL authentication, the public certificate of the {{product_name}} has to be imported to the truststore of the client and the public certificate of the client to the {{product_name}}'s client-truststore. + + To achieve this, execute the following commands from the `/repository/resources/security` directory. + + === "Export to client truststore" + + ``` java + keytool -export -alias wso2carbon -file carbon_public2.crt -keystore wso2carbon.jks -storepass wso2carbon + ``` + + === "Import to WSO2 Identity Server truststore" + + ``` java + keytool -import -trustcacerts -alias carbon -file carbon_public2.crt -keystore client-truststore.jks -storepass wso2carbon + ``` + + !!! note + If you are not using mutual SSL authentication, you can stop the **MutualSSLManager** from loading the keystore by setting the `mutual_ssl_manager_enabled` property in the `/repository/conf/deployment.toml` file to false. This property is enabled by default. + + ``` toml + mutual_ssl_manager_enabled="false" + ``` + + Alternatively, if the authentication endpoint is hosted externally, then set the `mutualSSLManagerEnabled` property to false in `/authenticationendpoint/WEB-INF/classes/EndpointConfig.properties` file. + + ``` toml + mutualSSLManagerEnabled=false + ``` + + Make sure to restart the server to apply the configuration changes. + +--- +## Disable multitenancy for the login page + +If you want to disable the tenant domain dropdown list from your SSO login page, follow the steps below. + +1. Shutdown {{product_name}} if it is already started. + +2. Navigate to the `deployment.toml` file found in the `/repository/conf` directory and do the following: + + - Set the `tenant_list_enabled` property to **false**. + + ``` toml + [identity.auth_framework.endpoint] + tenant_list_enabled = "false" + ``` + + !!! note + + If the authentication endpoint is hosted externally, then set the `tenantListEnabled` + property to false in `/authenticationendpoint/WEB-INF/classes/EndpointConfig.properties` file. + + ```bash + tenantListEnabled=false + ``` + + - Set the following parameter to `false` in the same `deployment.toml` file. + + ``` toml + [tenant.domain_drop_down] + enable = false + ``` + + - If you only used `MutualSSLAuthenticator` to list tenant domains, disable it. + + ``` toml + [admin_console.authenticator.mutual_ssl_authenticator] + enable = false + ``` + +3. Restart the server. + +--- + +## Handle browser back buttons and bookmarked login pages + +To gracefully handle scenarios where the user may click the back button from an authenticated application or decides to bookmark the endpoint login page URL, you need to redirect the users to the access URL of the application. To do so, + +1. On the {{product_name}} Console, go to **Applications** and select your application. + +2. Specify the access URL of your application as shown below. + + ![service provider access url]({{base_path}}/assets/img/references/`add-access-url-to-app`.png){: style="border: 0.3px solid lightgrey;"} + +3. Click **Update** to save the changes. + +## Add Custom Filters and Listeners + +Filters can be used to perform various pre-processing and post-processing tasks on the requests and responses such as logging and input validation. To configure custom filters, add the following configurations to the `/repository/conf/deployment.toml` directory. + +``` toml +[[authenticationendpoint.filter]] +name="CustomFilter" +class="org.wso2.CustomFilter" +url_pattern="/*" +dispatchers=["FORWARD", "REQUEST"] + +[[authenticationendpoint.filter.init_param]] +name="ParamName" +value= "ParamValue" +``` + +Listeners can be used to monitor and respond to specific events such as the creation or destruction of servlet contexts, sessions, or request objects. To configure custom listeners, add the following configuration in the `/repository/conf/deployment.toml` file. + +``` toml +[[authenticationendpoint.listener]] +class="org.wso2.CustomListener" +``` \ No newline at end of file diff --git a/en/includes/references/extend/authentication/oauth2/write-a-custom-oauth-2.0-grant-type.md b/en/includes/references/extend/authentication/oauth2/write-a-custom-oauth-2.0-grant-type.md new file mode 100644 index 0000000000..f56c97918e --- /dev/null +++ b/en/includes/references/extend/authentication/oauth2/write-a-custom-oauth-2.0-grant-type.md @@ -0,0 +1,337 @@ +# Write a Custom OAuth 2.0 Grant Type + +OAuth 2.0 authorization servers provide support for four main grant +types according to the [OAuth 2.0 specification](https://tools.ietf.org/html/rfc6749){:target="_blank"}. It +also has the flexibility to support any custom grant types. This topic +provides instructions on how to implement a custom grant type for OAuth +2.0 authorization server and how to extend the behavior of default grant +types. + +The WSO2 Identity Server is used as the OAuth 2.0 authorization server +implementation, which is an open source implementation. + + +## Implement a new grant type + +Follow the steps given below to implement a new grant type. + +1. Implement the following two extensions. + - ` GrantTypeHandler ` - This is the + implementation of the grant type. Here you can implement the + way, it must be validated and how token must be issued. You can + write the new implementation by implementing the + ` AuthorizationGrantHandler ` interface + or by extending + ` AbstractAuthorizationGrantHandler ` . In + most cases, it is enough to extend the + ` AbstractAuthorizationGrantHandler ` in + the WSO2 OAuth component. + - ` GrantTypeValidator ` - This is used to + validate the grant request that is sent to the + ` /token ` endpoint. You can define what + parameters must be in the request and define the validation of + them. You can write the new implementation by extending the + ` AbstractValidator ` in Apache Amber + component. +2. When implementation is done, package your class as a .jar file and + place it in the + ` /repository/component/lib ` directory. +3. To register the custom grant type, configure the + ` /repository/conf/deployment.toml ` + file by adding a new entry, in a manner similar to the following + example; + + ```toml + [[oauth.custom_grant_type]] + name="grant type identifier" + grant_handler="full qualified class name of grant handler" + grant_validator="full qualified class name of grant validator" + [oauth.custom_grant_type.properties] + IdTokenAllowed=true + PublicClientAllowed=true + ``` + + !!! info + Setting the ` ` parameter to + ` true `, provides flexibility to control the + issuing of IDtoken for each grant, and also allows the OIDC scope + validator to validate the grant types that should support the openid + scope. + + Setting the ` ` parameter to + ` true `, provides the capability to configure + whether the grant type can be used by public clients or not. Failing to + set this parameter will result in the grant type being not supported for + public clients. + +4. Restart the server to apply changes. + +To test this out, follow the instructions below to implement a custom-grant type sample. + +## Use the grant type sample + +The sample demonstrated here defines a new sample grant type called the +"mobile" grant type. It is similar to the password grant type and the +only difference is that a mobile number will be passed through instead +of a password. The request to the ` /token ` API must +contain the following two request parameters. + +- ` grant_type=mobile ` +- ` mobileNumber=044322433 ` + +You can access the new grant type project sample from +[here](https://github.com/wso2/samples-is/tree/v4.6.0/oauth2/custom-grant){:target="_blank"} +. The grant handler and validator class is inside the +` org.wso2.sample.identity.oauth2.grant.mobile ` package. +This can be modified as required. + +### Configure the resource + +The following Maven buildable source is compatible with WSO2 Identity +Server 5.1.0 onwards. The attached ` .jar ` file can be +directly used as well. + +| Buildable source | [custom-grant](https://github.com/wso2/samples-is/tree/v4.6.0/oauth2/custom-grant){:target="_blank"} | +|--------------------|------------------------------------------------------------------------------------| +| Built jar file | [custom-grant-4.5.6.jar]({{base_path}}/assets/attachments/custom-grant-4.5.6.jar){:target="_blank"} | + +!!! note + To generate the .jar file from the buildable source, run the following Apache Maven + command in the sample's location using the command line. + + ``` java + mvn clean install + ``` + +1. Copy the .jar file in target directory into the + ` /repository/component/lib ` + directory. +2. Configure the following in the +` /repository/conf/deployment.toml ` + file. + ```toml + [[oauth.custom_grant_type]] + name="mobile" + grant_handler="org.wso2.sample.identity.oauth2.grant.mobile.MobileGrant" + grant_validator="org.wso2.sample.identity.oauth2.grant.mobile.MobileGrantValidator" + [oauth.custom_grant_type.properties] + IdTokenAllowed=true + PublicClientAllowed=true + ``` + +3. Restart the server. + +### Configure an application with the custom grant type + +1. On the WSO2 Identity Server Console, go to **Applications**. + +2. Click **New Application** and select **Standard-Based Application**. + +3. Provide an application name and select **OIDC** as the protocol. + +4. Click **Register** to complete the registration. + +5. In the **Protocol** tab, select the custom grant type (mobile) from the **Allowed Grant Types** list. + +6. Click **Update**. Note the **Client ID** and **Client secret** that appear. + + !!! tip + To configure more advanced configurations, see [OIDC Configurations]({{base_path}}/references/app-settings/oidc-settings-for-app). + +### Obtain an access token + +Send the grant request to the `/token` API using a cURL command. + +!!! note + The HTTP POST body must contain the following two parameters: `grant_type=mobile` and `mobileNumber`. + + ``` + grant_type=mobile&mobileNumber= + ``` + +!!! abstract "" + **Request format** + ``` + curl --user : -k -d "grant_type=&mobileNumber=" -H "Content-Type: application/x-www-form-urlencoded" https://:/oauth2/token + ``` + --- + **Sample request** + ```curl + curl --user 7wYeybBGCVfLxPmS0z66WNMffyMa:WYfwHUsbsEvwtqmDLuaxF_VCQJwa -k -d "grant_type=mobile&mobileNumber=0333444" -H "Content-Type: application/x-www-form-urlencoded" https://localhost:9443/oauth2/token + ``` + +You will receive a response similar to the following JSON response with the access token. + +``` +{ + "access_token": "d9b46507-0300-4a8e-839f-0b856a74c481", + "refresh_token": "45a82bd4-ef55-4368-bc20-9cbc240308d1", + "token_type": "mobile", + "expires_in": 10000 +} +``` + +!!! note "Note" + The access token and the refresh token that are received with the response are just randomly generated by the sample application. Those tokens are not valid tokens. + +## Customize an existing grant type + +As an alternative to creating a custom OAuth grant type, you can +customize one of the existing grant types. The following two classes are +sample implementations of customizing the password grant type in +particular but any other grant type can be customized as well. + +- [RoleBasedPasswordGrant](https://github.com/wso2/samples-is/blob/v4.6.0/oauth2/custom-grant/src/main/java/org/wso2/sample/identity/oauth2/grant/password/RoleBasedPasswordGrant.java){:target="_blank"} - + This does some RBAC validation apart from the authentication before + granting access. +- [ModifiedAccessTokenPasswordGrant](https://github.com/wso2/samples-is/blob/v4.6.0/oauth2/custom-grant/src/main/java/org/wso2/sample/identity/oauth2/grant/password/ModifiedAccessTokenPasswordGrant.java){:target="_blank"} - + This implementation customized the access token value. + +### Configure the resource + +1. Copy the .jar file into the `/repository/component/lib` directory. You can also modify the project and build it using Apache Maven 3. +2. Configure the following in the `/repository/conf/deployment.toml` file. + ```toml + [oauth.grant_type.password] + grant_handler="org.wso2.sample.identity.oauth2.grant.password.ModifiedAccessTokenPasswordGrant" + ``` + +3. Restart the server. + +### Configure an application with the customized password grant type + +1. On the WSO2 Identity Server Console, go to **Applications**. + +2. Click **New Application** and select **Standard-Based Application**. + +3. Provide an application name and select **OIDC** as the protocol. + +4. Click **Register** to complete the registration. + +5. In the **Protocol** tab, select the **Password** grant type from the **Allowed Grant Types** list. + +6. Click **Update**. Note the **Client ID** and **Client secret** that appear. + + !!! tip + To configure more advanced configurations, see [OIDC Configurations]({{base_path}}/references/app-settings/oidc-settings-for-app). + +### Obtain an access token + +Send the password grant request to the `/token` API using a cURL command. + +!!! abstract + **Request format** + ``` + curl --user : -k -d "grant_type=password&username=&password=" -H "Content-Type: application/x-www-form-urlencoded" https://:/oauth2/token + ``` + --- + **Sample request** + ```curl + curl --user 7wYeybBGCVfLxPmS0z66WNMffyMa:WYfwHUsbsEvwtqmDLuaxF_VCQJwa -k -d "grant_type=password&username=admin&password=admin" -H "Content-Type: application/x-www-form-urlencoded" https://localhost:9443/oauth2/token + ``` + +You will receive a response similar to the following JSON response with the access token. + +``` +{ + "token_type":"bearer", + "expires_in":2955, + "refresh_token":"6865c8d67b42c0c23e634a8fc5aa81f", + "access_token":"982f40d4-0bb6-41ce-ac5a-1da06a83e475asela@soasecurity.org" +} +``` + +??? note "Sending Custom Error Codes" + + You can send custom + error codes to the client side in case of defined/identified errors, + using a custom grant handler. + + This simply uses the mobile grant sample and adds the specific error + codes/messages to it. + + !!! tip + See [here](#use-the-grant-type-sample) for instructions on how to implement and use the mobile grant + sample before adding the custom error codes to it. + + In the downloaded Maven [buildable source](https://github.com/wso2/samples-is/tree/v4.6.0/oauth2/custom-grant){:target="_blank"}, the following code segment is added in the sample class + ` org .wso2.sample.identity.oauth2.grant.mobile.MobileGrant ` + inside ` validateGrant() ` method, which is the relevant + code used for this mechanism. + + ``` java + if(mobileNumber != null) { + //validate mobile number + authStatus = isValidMobileNumber(mobileNumber); + + if(authStatus) { + // if valid set authorized mobile number as grant user + AuthenticatedUser mobileUser = new AuthenticatedUser(); + mobileUser.setUserName(mobileNumber); + oAuthTokenReqMessageContext.setAuthorizedUser(mobileUser); + oAuthTokenReqMessageContext.setScope(oAuthTokenReqMessageContext.getOauth2AccessTokenReqDTO().getScope()); + } else{ + ResponseHeader responseHeader = new ResponseHeader(); + responseHeader.setKey("SampleHeader-999"); + responseHeader.setValue("Provided Mobile Number is Invalid."); + oAuthTokenReqMessageContext.addProperty("RESPONSE_HEADERS", new ResponseHeader[]{responseHeader}); + } + + } + ``` + + !!! note + + The `ResponseHeader` code chunk sets a custom response header in case an + invalid mobile number is sent. + + Build the project and copy the modified jar to the `/repository/component/lib` directory. + + **Try it out** + + - Happy Path + + ``` powershell + curl --user : -k -d "grant_type=mobile&mobileNumber=0333444" -H "Content-Type: application/x-www-form-urlencoded" https://:/oauth2/token + ``` + + - Erroneous Path + + ``` powershell + curl -v --user : -k -d "grant_type=mobile&mobileNumber=0363444" -H "Content-Type: application/x-www-form-urlencoded" https://:/oauth2/token + ``` + + !!! info + This is erroneous according to the mobile custom grant sample because + the mobile number does not start with '003'. You can use the -v option + in the cURL command to see the header of the response which should be as + seen in the code block below. + + ``` powershell + > POST /oauth2/token HTTP/1.1 + > Authorization: Basic dlNmZVE5amZOb2RZMXR2OUtMTk54TE93N2t3YTpDRVVXdTdmRE55X1JZZzVsT19tcDhQTGY3blFh + > User-Agent: curl/7.29.0 + > Host: localhost:9443 + > Accept: */* + > Content-Type: application/x-www-form-urlencoded + > Content-Length: 38 + > + * upload completely sent off: 38 out of 38 bytes + < HTTP/1.1 400 Bad Request + < Date: Wed, 13 Jan 2016 06:05:33 GMT + < SampleHeader-999: Provided Mobile Number is Invalid. + < Content-Type: application/json + < Content-Length: 87 + < Connection: close + < Server: WSO2 Carbon Server + < + * Closing connection 0 + * SSLv3, TLS alert, Client hello (1): + {"error":"invalid_grant","error_description":"Provided Authorization Grant is invalid"} + ``` + + !!! info + `SampleHeader-999...` shows the custom header appearing in the headers. + + Similarly this can be used to transfer any custom information to the + client, in a flexible manner. diff --git a/en/includes/references/extend/authentication/write-a-custom-local-authenticator.md b/en/includes/references/extend/authentication/write-a-custom-local-authenticator.md new file mode 100644 index 0000000000..8d0e1aa33a --- /dev/null +++ b/en/includes/references/extend/authentication/write-a-custom-local-authenticator.md @@ -0,0 +1,146 @@ +# Write a Custom Local Authenticator + +A local authenticator in WSO2 identity server authenticates users, who are stored in the local database, using a defined authentication logic. + +The default local authenticator in WSO2 identity server is the `Username & Password` authenticator. +It authenticates end users stored in a connected user store using the provided username and password. + +WSO2 identity server supports extensibility in local authentication so that you can implement a different authentication logic by writing a custom local authenticator. +You can implement custom authentication logic tailored to your requirement, as follows: + +1. Authenticating users only if they belong to a particular role. +2. Authenticating based on any claim, such as the user's telephone number or employee registration number. +3. Calling another API to authenticate the user. + +--- + +## Scenario + +You have a sample app to which you want users to log in with their telephone numbers instead of usernames. Once a user enters a telephone number, your authentication logic should identify the user and validate the user's credentials. + +The following guide shows you how to write a custom local authenticator to implement this authentication logic. + +--- + +## Write the local authenticator + +To write the local authenticator: + +1. Create a `maven` project for the custom local authenticator. Refer the following files: + - The [pom.xml](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.local.authenticator/pom.xml){:target="_blank"} file used for the sample custom local authenticator. + - The [service component class](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.local.authenticator/src/main/java/org/wso2/carbon/identity/sample/local/authenticator/internal/SampleLocalAuthenticatorServiceComponent.java){:target="_blank"} file to deploy in WSO2 Identity Server and register it as a local authenticator. + +2. Write the [custom local authenticator](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.local.authenticator/src/main/java/org/wso2/carbon/identity/sample/local/authenticator/SampleLocalAuthenticator.java){:target="_blank"} by extending the +[AbstractApplicationAuthenticator](https://github.com/wso2/carbon-identity-framework/blob/v7.0.12/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/AbstractApplicationAuthenticator.java){:target="_blank"} class and implementing the +[LocalApplicationAuthenticator](https://github.com/wso2/carbon-identity-framework/blob/v7.0.12/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/LocalApplicationAuthenticator.java){:target="_blank"} class. + +!!! info + See the sample [custom local authenticator](https://github.com/wso2/samples-is/tree/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.local.authenticator){:target="_blank"} for your reference. + +??? "Methods in `AbstractApplicationAuthenticator` class and `LocalApplicationAuthenticator` interface" + | Method | Description | + |-----------|---------------| + |[**public String getName()**](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.local.authenticator/src/main/java/org/wso2/carbon/identity/sample/local/authenticator/SampleLocalAuthenticator.java#L159){:target="_blank"} | Return the name of the authenticator.

In the sample project, we have used this method to return the name SampleLocalAuthenticator.

| + |[**public String getFriendlyName()**](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.local.authenticator/src/main/java/org/wso2/carbon/identity/sample/local/authenticator/SampleLocalAuthenticator.java#L165){:target="_blank"} | Returns the display name for the custom local authenticator.

In the sample project, we have returned the name sample-local-authenticator.

| + | [**public String getContextIdentifier(HttpServletRequest request**)](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.local.authenticator/src/main/java/org/wso2/carbon/identity/sample/local/authenticator/SampleLocalAuthenticator.java#L153){:target="_blank"} | Returns a unique identifier that will map the authentication request and the response. The value returned by the invocation of the authentication request and the response should be the same. | + | [public boolean canHandle(HttpServletRequest request)](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.local.authenticator/src/main/java/org/wso2/carbon/identity/sample/local/authenticator/SampleLocalAuthenticator.java#L58-L63){:target="_blank"}|

This method checks whether the authentication request is valid, according to the custom authenticator’s requirements. The user will be authenticated if the method returns 'true'. This method also checks whether the authentication or logout request can be handled by the authenticator.

In our sample project, we used this method to check if the username and password are 'not null' in the authentication request. If that succeeds, the authentication flow will continue.

| + | [protected void initiateAuthenticationRequest(HttpServletRequest request,HttpServletResponse response, AuthenticationContext context)](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.local.authenticator/src/main/java/org/wso2/carbon/identity/sample/local/authenticator/SampleLocalAuthenticator.java#L66-L67){:target="_blank"} |

This method is used to redirect the user to the login page to authenticate. You can redirect the user to a custom login URL using this method or you can use the default WSO2 Identity Server login page.

| + | [protected void processAuthenticationResponse(HttpServletRequest request,HttpServletResponse response, AuthenticationContext context)](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.local.authenticator/src/main/java/org/wso2/carbon/identity/sample/local/authenticator/SampleLocalAuthenticator.java#L89-L90){:target="_blank"} |

Implementation of custom authentication logic happens inside this method. For example, you can call any API that can do authentication and authenticate the user or you can authenticate the user against the underlying user store. Then you can also do any other custom logic after authenticating the user such as, you can check if a user belongs to a particular role and allowing authentication accordingly.

In the sample project, we used this method to authenticate the user with the user's telephone number and password, and make the authentication successful.

| + | public AuthenticatorFlowStatus process(HttpServletRequest request, HttpServletResponse response, AuthenticationContext context) |

This method is used to process or carry out the user authentication process. It calls the processAuthenticationResponse() method in the custom authenticator class to execute the custom authentication logic.

In the sample project, we call the super.process(), so that the super class process method will handle the authentication process, instead of implementing our own process method.

+ | protected boolean retryAuthenticationEnabled(AuthenticationContext context) |

This method returns a boolean value. If the authentication fails due to some issue like invalid credentials, this method will be called to know whether to retry the authentication flow. If this method returns 'true', the authentication flow will be retried. If returns 'false', the flow is stopped with an exception thrown.

You need to override this method if you are calling the super.process() method. But if you are writing your own process() method, you can handle all the retrying logic accordingly within the method.

| + +--- + +## Deploy the authenticator + +To deploy the custom local authenticator: + +1. Open a terminal, navigate to the root of your project, and compile the service by running the following command: + + ``` xml + mvn clean install + ``` + +2. Copy the generated `org.wso2.carbon.identity.sample.local.authenticator-1.0.0.jar` file from `/target` folder to the `/repository/components/dropins` folder. +3. Add the following config to the deployment.toml file in the `/repository/conf` folder. The name should be the authenticator name returned by the `getName()` method in the authenticator class. + + ``` toml + [authentication.authenticator.sample_authenticator] + name = "SampleLocalAuthenticator" + enable = true + ``` + +--- + +## Configure the Identity Server + +This section guides you on how to configure the identity server to use the custom local authenticator. + +### Prerequisites +- You need to [set up the sample]({{base_path}}/get-started/try-samples/qsg-oidc-webapp-java-ee) application. + +### Configure the application with the custom local authenticator + +To configure the custom local authenticator for the application: + +1. On the WSO2 Identity Server console, go to **Applications**. + +2. Select your application, go to the **Login Flow** tab and add the custom local authenticator from your preferred editor: + + --- + === "Classic Editor" + To add custom local authenticator using the Classic Editor: + + 1. If you haven't already defined a sign-in flow, click **Start with Default configuration** to get started. + + 2. Remove the existing **Username & Password** authenticator and click **Add Authentication** on the step, select your custom local authenticator, and click **Add**. + + ![Add custom federated authenticator]({{base_path}}/assets/img/guides/idp/custom-connector/add-custom-local-authenticator-with-classic-editor.png){: width="700" style="display: block; margin: 0; border: 0.3px solid lightgrey;"} + + === "Visual Editor" + To add custom local authenticator using the Visual Editor: + + 1. Switch to the **Visual Editor** tab, by default the `Username & Password` login flow will be added onto the Visual Editor's workspace. + + 2. Remove the existing **Username & Password** authenticator and click on `+ Add Sign In Option` to add a new authenticator to the same step and select your custom local authenticator. + + ![Add custom local authenticator using the Visual Editor]({{base_path}}/assets/img/guides/idp/custom-connector/add-custom-local-authenticator-with-visual-editor.png){: width="500" style="display: block; margin: 0; border: 0.3px solid lightgrey;"} + + --- + +3. Click **Update** to save your changes. + +--- + +### Add a new user + +To add a new user: + +1. On the WSO2 Identity Server console, go to **User Management** > **Users**. + +2. Click **Add User** and provide the required details. + +3. Add a telephone number to the **Mobile** field and click **Next**. + +5. Add the user to a group, if necessary, and click **Next**. + +6. Assign the user a role, if necessary, and click **Next**. + +7. Review the summary and click **Finish**. + +--- +## Try it out + +1. Access the application URL. + +2. Click **Login**. You will be redirected to the WSO2 Identity Server login page. + +3. Enter the user's **telephone number** as the identifier and enter the user's password, and click **Continue**. + +The user will be successfully authenticated. + +--- + +## Contributing your connectors + +{% include "../../../guides/fragments/contributing-connectors.md" %} diff --git a/en/includes/references/extend/federation/write-a-custom-federated-authenticator.md b/en/includes/references/extend/federation/write-a-custom-federated-authenticator.md new file mode 100644 index 0000000000..9280ff06d9 --- /dev/null +++ b/en/includes/references/extend/federation/write-a-custom-federated-authenticator.md @@ -0,0 +1,306 @@ +# Write a Custom Federated Authenticator + +A custom federated authenticator can be written to authenticate a user with an external system. +The external system can be any identity provider such as Facebook, Twitter, Google, Yahoo, etc. +You can use the extension points available in WSO2 Identity Server to create custom federated authenticators. + +![Federated authentication diagram]({{base_path}}/assets/img/references/extend/federated-authentication-diagram.png){: width="1000" style="display: block; margin: 0;"} + +--- + +## Authenticator API + +The following is the API used to implement a custom federated +authenticator. + +??? example "Click to view the API of the Application Authenticators" + ``` java + /* + * Copyright (c) 2005-2013, 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.carbon.identity.application.authentication.framework; + + import java.io.Serializable; + import java.util.List; + + import javax.servlet.http.HttpServletRequest; + import javax.servlet.http.HttpServletResponse; + + import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; + import org.wso2.carbon.identity.application.authentication.framework.exception.AuthenticationFailedException; + import org.wso2.carbon.identity.application.authentication.framework.exception.LogoutFailedException; + import org.wso2.carbon.identity.application.common.model.Property; + + /** + * API of the Application Authenticators. + * + */ + public interface ApplicationAuthenticator extends Serializable { + + /** + * Check whether the authentication or logout request can be handled by the + * authenticator + * + * @param request + * @return boolean + */ + public boolean canHandle(HttpServletRequest request); + + /** + * Process the authentication or logout request. + * + * @param request + * @param response + * @param context + * @return the status of the flow + * @throws AuthenticationFailedException + * @throws LogoutFailedException + */ + public AuthenticatorFlowStatus process(HttpServletRequest request, + HttpServletResponse response, AuthenticationContext context) + throws AuthenticationFailedException, LogoutFailedException; + + /** + * Get the Context identifier sent with the request. This identifier is used + * to retrieve the state of the authentication/logout flow + * + * @param request + * @return + */ + public String getContextIdentifier(HttpServletRequest request); + + /** + * Get the name of the Authenticator + * @return name + */ + public String getName(); + + /** + * @return + */ + public String getFriendlyName(); + + /** + * Get the claim dialect URI if this authenticator receives claims in a standard dialect + * and needs to be mapped to the Carbon dialect http://wso2.org/claims + * @return boolean + */ + public String getClaimDialectURI(); + + /** + * @return + */ + public List getConfigurationProperties(); + } + ``` + +--- + +## Write the federated authenticator + +To write the federated authenticator: + +1. Create a `maven` project for the custom federated authenticator. Refer the following files: + - The [pom.xml](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.federated.authenticator/pom.xml){:target="_blank"} file used for the sample custom federated authenticator. + - The [service component class](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.federated.authenticator/src/main/java/org/wso2/carbon/identity/custom/federated/authenticator/internal/CustomFederatedAuthenticatorServiceComponent.java){:target="_blank"} file to deploy in WSO2 Identity Server and register it as a federated authenticator. + +2. Write the [custom federated authenticator](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.federated.authenticator/src/main/java/org/wso2/carbon/identity/custom/federated/authenticator/CustomFederatedAuthenticator.java){:target="_blank"} by extending the +[AbstractApplicationAuthenticator](https://github.com/wso2/carbon-identity-framework/blob/v5.18.187/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/AbstractApplicationAuthenticator.java){:target="_blank"} class and implementing the +[FederatedApplicationAuthenticator](https://github.com/wso2/carbon-identity-framework/blob/v5.18.187/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/FederatedApplicationAuthenticator.java){:target="_blank"} class. + +!!! info + See the sample [custom federated authenticator](https://github.com/wso2/samples-is/tree/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.federated.authenticator){:target="_blank"} for your reference. + +??? "Methods in `AbstractApplicationAuthenticator` class and `FederatedApplicationAuthenticator` interface" + | Method | Description | + |-----------|---------------| + |[**public String getName()**](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.federated.authenticator/src/main/java/org/wso2/carbon/identity/custom/federated/authenticator/CustomFederatedAuthenticator.java#L72-L75){:target="_blank"} | Return the name of the authenticator. | + |[**public String getFriendlyName()**](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.federated.authenticator/src/main/java/org/wso2/carbon/identity/custom/federated/authenticator/CustomFederatedAuthenticator.java#L66-L69){:target="_blank"} | Returns the display name for the custom federated authenticator. In this sample, we are using a custom-federated-authenticator. | + | [**public String getContextIdentifier(HttpServletRequest request**)](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.federated.authenticator/src/main/java/org/wso2/carbon/identity/custom/federated/authenticator/CustomFederatedAuthenticator.java#L214-L222){:target="_blank"} | Returns a unique identifier that will map the authentication request and the response. The value returned by the invocation of the authentication request and the response should be the same. | + | [public boolean canHandle(HttpServletRequest request)](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.federated.authenticator/src/main/java/org/wso2/carbon/identity/custom/federated/authenticator/CustomFederatedAuthenticator.java#L59-L63){:target="_blank"}|Specifies whether this authenticator can handle the authentication response. | + | [protected void initiateAuthenticationRequest(HttpServletRequest request,HttpServletResponse response, AuthenticationContext context)](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.federated.authenticator/src/main/java/org/wso2/carbon/identity/custom/federated/authenticator/CustomFederatedAuthenticator.java#L136-L137){:target="_blank"} | Redirects the user to the login page to authenticate and in this sample, the user is redirected to the login page of the application which is configured in the partner identity server which acts as the external service. | + | [protected void processAuthenticationResponse(HttpServletRequest request,HttpServletResponse response, AuthenticationContext context)](https://github.com/wso2/samples-is/blob/v4.6.0/authenticators/components/org.wso2.carbon.identity.sample.federated.authenticator/src/main/java/org/wso2/carbon/identity/custom/federated/authenticator/CustomFederatedAuthenticator.java#L168-L169){:target="_blank"} | Implements the logic of the custom federated authenticator. | + +--- + +## Deploy the authenticator + +To deploy the custom federated authenticator: + +1. Open a terminal, navigate to the root of your project, and compile the service by running the following command: + + ``` xml + mvn clean install + ``` + +2. Copy the generated `org.wso2.carbon.identity.custom.federated.authenticator-1.0.0.jar` file from `/target` folder to the `/repository/components/dropins` folder. + +--- + +## Configure the partner identity server + +In this guide, the partner identity server acts as the external system. +Therefore, it is required to run a second WSO2 Identity Server instance as the partner identity server. + +### Set up partner IS +Add the following config to the `/repository/conf/deployment.toml` file to be able to run the partner identity server on the same machine in a different port. + +``` toml +[server] +offset=1 +``` + +After starting that partner identity server, it will run on [localhost:9444](https://localhost:9444/console). + + +### Register an application + +To register and configure the application on the partner IS: + +1. On the WSO2 Identity Server console of the partner identity server, go to **Applications**. + +2. Click **New Application** and select **Traditional Web Application**. + +3. Enter the following details: + + + + + + + + + + + + + + +
NameGive a unique name to identify your application.
ProtocolSelect OpenID Connect.
Authorized redirect URLs + The URL to which the user is redirected after a successful login. Use the following URL for this sample app: +

https://localhost:9443/commonauth

+
+ +5. Click **Register**. + +6. Go to the **Protocol** tab and take note of the **Client ID** and the **Client secret**. + +!!! info "Download and configure the application" + On the partner identity server: + + - Download the [sample](https://github.com/asgardeo/asgardeo-tomcat-oidc-agent/releases/latest/download/oidc-sample-app.war){:target="_blank"} application. + - [Configure the sample OIDC]({{base_path}}/get-started/try-samples/qsg-oidc-webapp-java-ee/#configure-the-sample) application on the partner identity server. + +### Add a new user + +To add a new user: + +1. On the WSO2 Identity Server console of the partner identity server, go to **User Management** > **Users**. + +2. Click **Add User** and provide the required details. + +3. Click **Next**. + +4. Add the user to a group, if necessary, and click **Next**. + +5. Assign the user a role, if necessary, and click **Next**. + +6. Review the summary and click **Finish**. + +--- + +## Configure the Identity Server + +This section guides you on how to configure the identity server to use the custom federated authenticator. + +### Prerequisites +- You need to [set up the sample]({{base_path}}/get-started/try-samples/qsg-oidc-webapp-java-ee) application. + +### Configure federated authenticator + +To configure the custom fedrated authenticator: + +1. On the WSO2 Identity Server console, go to **Connections**. +2. Click **Create Connection** and select **Custom Connector**. +3. Provide a unique name and a description and click **Finish**. +4. Go to **Settings** tab and click **New Authenticator**. +5. Select the custom federated authenticator added and click **Next**. +6. Enter the following details: + + | Field name | Value | + |---------------|-------| + | **Client Id** | The Client Id value generated by the external application of the partner identity server. | + | **Client Secret** | The Client Secret value generated by the external application of the partner identity server. | + | **Callback URL** | `https://localhost:9443/commonauth` | + | **Authorization Endpoint URL** | `https://localhost:9444/oauth2/authorize/` | + | **Token Endpoint URL** | `https://localhost:9444/oauth2/token/` | + + ![Federated Authenticator]({{base_path}}/assets/img/references/extend/federated-authenticator.png){: width="800" style="display: block; margin: 0; border: 0.3px solid lightgrey;"} + +5. Click **Finish** to save the configurations. + +### Configure the application with the custom federated authenticator + +To configure the custom federated authenticator for the application: + +1. On the WSO2 Identity Server console, go to **Applications**. + +2. Select your application, go to the **Login Flow** tab and add the custom federated authenticator from your preferred editor: + + --- + === "Classic Editor" + To add custom federated authenticator using the Classic Editor: + + 1. If you haven't already defined a sign-in flow, click **Start with Default configuration** to get started. + + 2. Click **Add Authentication** on the step, select your custom federated authenticator, and click **Add**. + + ![Add custom federated authenticator]({{base_path}}/assets/img/guides/idp/custom-connector/add-custom-federation-with-basic.png){: width="700" style="display: block; margin: 0; border: 0.3px solid lightgrey;"} + + === "Visual Editor" + To add custom federated authenticator using the Visual Editor: + + 1. Switch to the **Visual Editor** tab, by default the `Username & Password` login flow will be added onto the Visual Editor's workspace. + + 2. Click on `+ Add Sign In Option` to add a new authenticator to the same step and select your custom federated authenticator. + + ![Add custom federated authenticator using the Visual Editor]({{base_path}}/assets/img/guides/idp/custom-connector/add-custom-federation-with-visual-editor.png){: width="500" style="display: block; margin: 0; border: 0.3px solid lightgrey;"} + + --- + +3. Click **Update** to save your changes. + +--- + +## Try it out + +1. Access the application URL. + +2. Click **Login**. You will be redirected to the WSO2 Identity Server login page. + +3. Select the sign in option configured. + +4. Enter the credentials of the the new user created in the partner identity server. + +The user is successfully authenticated by the partner Identity Server. + +Similarly, you can write a federated authenticator to authenticate the users using an external system. + +## Contributing your connectors + +{% include "../../../guides/fragments/contributing-connectors.md" %} \ No newline at end of file diff --git a/en/includes/references/extend/index.md b/en/includes/references/extend/index.md new file mode 100644 index 0000000000..357db0161f --- /dev/null +++ b/en/includes/references/extend/index.md @@ -0,0 +1,11 @@ +# Extensions + +This section discuss how WSO2 Identity Server can be extended related to following aspects. + +- [Authentication]({{base_path}}/references/extend/authentication/oauth2/write-a-custom-oauth-2.0-grant-type) + +- [Identity Federation]({{base_path}}/references/extend/federation/write-a-custom-federated-authenticator) + +- [User Management]({{base_path}}/references/extend/user-mgt/write-a-custom-event-handler) + +- [User Stores]({{base_path}}/references/extend/user-stores/write-a-custom-user-store-manager) \ No newline at end of file diff --git a/en/includes/references/extend/user-mgt/provisioning/extend-scim2-user-schemas.md b/en/includes/references/extend/user-mgt/provisioning/extend-scim2-user-schemas.md new file mode 100644 index 0000000000..904e7b46c3 --- /dev/null +++ b/en/includes/references/extend/user-mgt/provisioning/extend-scim2-user-schemas.md @@ -0,0 +1,215 @@ +# Extend SCIM 2.0 user schemas + +The System for Cross-domain Identity Management (SCIM) is a specification that is designed to manage user identities in cloud-based applications and services in a standardized way to enable interoperability, security, and scalability. It is an emerging open standard which provides RESTful APIs for easier, cheaper, and faster way for creating, provisioning, and maintaining identities. The latest version SCIM 2.0 was released as IETF RFC in September 2015. + +!!! Note + SCIM 2.0 is supported by default in WSO2 Identity Server version 5.4.0 onwards. If you are using WSO2 Identity Server 5.4.0 or a later version, see SCIM 2.0 [Users REST API]({{base_path}}/apis/scim2-users-rest-apis), [Groups REST API]({{base_path}}/apis/scim2-groups-rest-apis), [Server Config REST API]({{base_path}}/apis/scim2-sp-config-rest-apis), [Me REST API]({{base_path}}/apis/scim2-me-rest-apis), and [Roles REST API]({{base_path}}/apis/roles-v2-rest-api/) for instructions on how to use SCIM 2.0 OOTB. + +--- + +## Extend the SCIM 2.0 API + +The SCIM 2.0 (System for Cross-Domain Identity Management) specification defines a [fixed set of default attributes](https://tools.ietf.org/html/rfc7643#section-8.2){:target="_blank"} for the user object. This set is defined to ensure the interoperability and it can cater to most of the industry's identity management requirements. However, in reality, organizations have their own attributes defined for their users which have already been used in their LDAP schemas. Therefore, SCIM is extensible enough to cope with custom attributes and uses the [Enterprise User Extension](https://tools.ietf.org/html/rfc7643#section-8.3){:target="_blank"} to support extra attributes for the SCIM user object. + +WSO2 Identity Server allows users to define their own user attributes in addition to the core user schema and enterprise user schema. These configured schema are then used while creating or validating user objects. This means that custom user attributes can be passed using SCIM for identity management requirements. + +## Extend the SCIM 2.0 API using the Enterprise User Extension + +!!! warning "Not recommended" + From {{product_name}} 6.0.0 onwards, it is recommended to use [SCIM2 Custom User Schema]({{base_path}}/guides/users/attributes/manage-scim2-attribute-mappings) to add custom attributes for users. + + Using enterprise user extension for custom user attributes is not recommended for the following reasons: +
    +
  • According to the current model, enterprise user extension attributes are contained in a file. Hence, any modifications to this file will be applied at the server level.
  • +
  • As Enterprise User Extension is a defined schema, it must remain unchanged.
  • +
+ +Follow the steps given below to add a custom attribute using the Enterprise User Extension.  + +1. Open the `scim2-schema-extension.config` file located in the `/repository/conf/` folder. + +2. Define the extension by adding attributes in the following format before the last element of the JSON array in + the `scim2-schema-extension.config` file. + + ``` java + { + "attributeURI":"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:customAttribute", + "attributeName":"customAttribute", + "dataType":"string", + "multiValued":"false", + "description":"customAttribute", + "required":"false", + "caseExact":"false", + "mutability":"readwrite", + "returned":"default", + "uniqueness":"none", + "subAttributes":"null", + "canonicalValues":[], + "referenceTypes":[] + } + ``` + +3. Add the attribute names of the attributes that you added to the `scim2-schema-extension.config` file as `subAttributes` of the `urn:ietf:params:scim:schemas:extension:enterprise:2.0:User` attribute as seen in the code block below. + + ``` java + { + "attributeURI":"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User", + "attributeName":"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User", + "dataType":"complex", + "multiValued":"false", + "description":"Enterprise User", + "required":"false", + "caseExact":"false", + "mutability":"readWrite", + "returned":"default", + "uniqueness":"none", + "subAttributes":"employeeNumber costCenter organization division department manager customAttribute", + "canonicalValues":[], + "referenceTypes":["external"] + } + ``` + +3. Save the file and restart WSO2 Identity Server.  + +--- + +## Add the custom attribute + +1. On the WSO2 Identity Server Console, go to **User Attributes & Stores** > **Attributes**. + +2. Click **Attributes** again under the **Manage Attributes** section. + +3. Click **New Attribute** and enter the following values.  + - **Attribute Name**: customAttribute + - **Attribute Display Name**: Custom Attribute + + !!! tip + Delete the SCIM 2.0 mapping under **Protocol Mappings** since this adds a custom schema. An enterprise schema will be added later in this guide. + + ![add-scim-local-custom-attribute]({{base_path}}/assets/img/references/extend/add-scim-local-custom-attribute.png){: width="600" style="display: block; margin: 0; border: 0.3px solid lightgrey;"} + +4. Click **Finish**.  + +!!! note + - The configuration above is valid when using the PRIMARY userstore. If you have a secondary userstore configured, make sure to add another attribute mapping for the particular user store. + +--- + +## Map the custom attribute + +1. On the WSO2 Identity Server Console, go to **User Attributes & Stores** > **Attributes**. + +2. Under **Manage Attribute Mappings**, click **SCIM 2.0**. + +3. Go to **Enterprise Schema** tab, click **New Attribute** and enter the following values. + - **SCIM Attribute**: customAttribute + - **User Attribute to map to**: Custom Attribute + + ![add-scim-external-attribute]({{base_path}}/assets/img/references/extend/add-scim-external-attribute.png){: width="600" style="display: block; margin: 0; border: 0.3px solid lightgrey;"} + +4. Click **Add Attribute Mapping**. Then click **Save**.  + +--- + +## Try it out + +1. On the WSO2 Identity Server Console, go to **User Management** > **Users**. + +2. Click **Add User**. + +3. Provide the required details and create the user. + +4. In the user profile, add a value for the **Custom Attribute** field and click **Update**. + +5. Retrieve the user using SCIM and ensure that the custom attribute value is present.  + + !!! abstract "" + **SCIM request** + ```bash + curl -v -k --user admin:admin https://localhost:9443/scim2/Users + ``` + --- + **SCIM response** + ``` + { + "totalResults":2, + "startIndex":1, + "itemsPerPage":2, + "schemas":["urn:ietf:params:scim:api:messages:2.0:ListResponse"], + "Resources":[ + { + "emails":["admin@wso2.com"], + "meta":{ + "created":"2020-01-21T16:52:11Z", + "lastModified":"2020-01-21T16:52:11Z" + }, + "roles":[{"type":"default", "value":"Internal/everyone,admin"}], + "name":{"givenName":"admin", "familyName":"admin"}, + "id":"54779523-8677-48bc-bce6-1764b7439f64", + "userName":"admin" + }, + { + "emails":[{"type":"home", "value":"testvalue"},"alex@gmail.com"], + "meta":{ + "created":"2020-01-21T16:56:56Z", + "lastModified":"2020-01-21T17:02:43Z", + "resourceType":"User" + }, + "EnterpriseUser":{"customAttribute":"testvalue"}, + "roles":[{"type":"default", "value":"Internal/everyone"}], + "name":{"givenName":"Alex", "familyName":"alex"}, + "id":"c91b5f1c-08c7-44d3-89c4-34ea9440dea5", + "userName":"alex" + } + ] + } + ``` + +You can also try out updating the custom attribute value using the following SCIM command and checking if the value is updated by accessing the user profile on the WSO2 Identity Server console.  + +!!! abstract "" + **Request format** + ``` + curl -k -X PATCH \ + https://:/scim2/Users/ \ + -H 'Authorization: Basic ' \ + -H 'Content-Type: application/json' \ + -d '{ + "schemas":[ + "urn:ietf:params:scim:api:messages:2.0:PatchOp" + ], + "Operations":[ + { + "op":"replace", + "value":{ + "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": { + "customAttribute": "" + } + } + } + ] + }' + ``` + --- + **Sample request** + ```curl + curl -k -X PATCH \ + https://localhost:9443/scim2/Users/be8f8562-7661-44a9-b6f4-d6d94dac914f \ + -H 'Authorization: Basic dmloYW5nYTphZG1pbg==' \ + -H 'Content-Type: application/json' \ + -d '{ + "schemas":[ + "urn:ietf:params:scim:api:messages:2.0:PatchOp" + ], + "Operations":[ + { + "op":"replace", + "value":{ + "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": { + "customAttribute": "new value" + } + } + } + ] + }' + ``` diff --git a/en/includes/references/extend/user-mgt/write-a-custom-event-handler.md b/en/includes/references/extend/user-mgt/write-a-custom-event-handler.md new file mode 100644 index 0000000000..00df5f7bdd --- /dev/null +++ b/en/includes/references/extend/user-mgt/write-a-custom-event-handler.md @@ -0,0 +1,136 @@ +# Write a custom event handler + +The WSO2 Identity Server eventing framework can be used to trigger events such as user operation events like `PRE_SET_USER_CLAIMS`,`POST_ADD_USER`. A full list of the sample events can be found below. The eventing framework also supports handlers which can be used to do operations upon a triggered event. For instance, an event handler can be used to validate the changed user password against previously used entries when a `PRE_UPDATE_CREDENTIAL` event is triggered. + +--- + +## What is an event handler? + +An event handler is used to perform an operation based on the published events. + +For instance, an event handler can be used to send an email after a user addition. The following sequence of operations are executed while adding a user. + +1. Publish the `PRE_ADD_USER` event — The subscribed handlers will be executed for the pre-add user event. +2. Execute the `AddUser` operation — The user will be persisted to the user store (LDAP or JBDC user store). +3. Publish the `POST_ADD_USER` event — The subscribed handlers will be executed for the post-add user event. + +Therefore, the email can be sent through an event handler that is subscribed to the `POST_ADD_USER` event. + +The following list is a list of sample events. + +- `AUTHENTICATION_STEP_SUCCESS` +- `AUTHENTICATION_STEP_FAILURE` +- `AUTHENTICATION_SUCCESS` +- `AUTHENTICATION_FAILURE` +- `PRE_AUTHENTICATION` +- `POST_AUTHENTICATION` +- `PRE_SET_USER_CLAIMS` +- `POST_SET_USER_CLAIMS` +- `PRE_ADD_USER` +- `POST_ADD_USER` + +!!! info + The other events available with WSO2 Identity Server can be found from the `Event` class [here](https://github.com/wso2/carbon-identity-framework/blob/master/components/identity-event/org.wso2.carbon.identity.event/src/main/java/org/wso2/carbon/identity/event/IdentityEventConstants.java){:target="_blank"}. + + +--- + +## Write an event handler + +To write a new event handler, you must extend the `org.wso2.carbon.identity.event.handler.AbstractEventHandler`. + +1. Override the `getName()` method to set the name for the event handler and the `getPriority()` method can be used to set the priory of the event handler. The handlers will be executed based on the priority. + + ```java + public String getName() { + return "customEventHandler"; + } + + @Override + public int getPriority(MessageContext messageContext) { + return 50; + } + ``` + +2. To execute the expected operation, override the `handleEvent()` method. The `event.getEventProperties()` method can be used to get the parameters related to the user operations. + The `handleEvent()` method should be called from the relevant method, which is written to execute a certain operation and the handlers will be executed once the operation is triggered. + ```java + @Override + public void handleEvent(Event event) throws IdentityEventException { + + Map eventProperties = event.getEventProperties(); + String userName = (String) eventProperties.get(IdentityEventConstants.EventProperty.USER_NAME); + UserStoreManager userStoreManager = (UserStoreManager) eventProperties.get(IdentityEventConstants.EventProperty.USER_STORE_MANAGER); + + String tenantDomain = (String) eventProperties.get(IdentityEventConstants.EventProperty.TENANT_DOMAIN); + String domainName = userStoreManager.getRealmConfiguration().getUserStoreProperty(UserCoreConstants.RealmConfig.PROPERTY_DOMAIN_NAME); + + String[] roleList = (String[]) eventProperties.get(IdentityEventConstants.EventProperty.ROLE_LIST); + ``` + +--- +## Register the event handler + +Register the event handler in the service component as follows. + +```java +protected void activate(ComponentContext context) { + try { + BundleContext bundleContext = context.getBundleContext(); + bundleContext.registerService(AbstractEventHandler. +class.getName(),new SampleEventHandler(), null); + } catch (Exception e) { + ... + } +``` + +--- + +## Configure the event handler + +Add the event handler configuration to the `/repository/conf/deployment.toml` file. The events that need to subscribe to the handler can be listed in subscriptions. + +```toml +[[event_handler]] +name= "customEventHandler" +subscriptions =["CUSTOM_EVENT"] +``` + +## Try out the sample application + +1. Build the sample custom identity event handler [here](https://github.com/wso2/samples-is/tree/v4.6.0/event-handler/custom-identity-event-handler){:target="_blank"} using `mvn clean install` command. +2. Copy the generated org.wso2.carbon.identity.customhandler-4.5.6.jar file in the target folder into `/repository/components/dropins/` folder. +3. Add following configurations to `/repository/conf/deployement.toml` file + ```toml + [[event_handler]] + name="customUserRegistration" + subscriptions=["PRE_ADD_USER","POST_ADD_USER"] + ``` + `name`: The name of the event handler (Name that return from the `getName()` method). + + `subscriptions`: A list of events that the handler will be subscribed to. In this sample application, we are subscribing to the `PRE_ADD_USER` and `POST_ADD_USER` events. + +4. Restart the server. + +!!! note "Note" + In this sample application, the event handler just prints the event properties in the console (Username & tenant-domain). But you can customize the event handler to do whatever you want (e.g., Send an email after adding a user). + +When you want to execute an operation related to an event, publish the event. Then, the handler that is subscribed for the relevant events will be used to execute those events. In the sample configuration given above, the `customUserRegistration` handler is subscribed to the `PRE_ADD_USER, POST_ADD_USER` operations. + + +!!! info + The following sample event handlers are available with WSO2 Identity Server. + + - [UserEmailVerificationHandler](https://github.com/wso2-extensions/identity-governance/blob/master/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/handler/UserEmailVerificationHandler.java){:target="_blank"} + + - [AccountConfirmationValidationHandler](https://github.com/wso2-extensions/identity-governance/blob/master/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/handler/AccountConfirmationValidationHandler.java){:target="_blank"} + + - [AdminForcedPasswordResetHandler](https://github.com/wso2-extensions/identity-governance/blob/master/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/handler/AdminForcedPasswordResetHandler.java){:target="_blank"} + + - [UserSelfRegistrationHandler](https://github.com/wso2-extensions/identity-governance/blob/master/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/handler/UserSelfRegistrationHandler.java){:target="_blank"} + + - [PasswordHistoryValidationHandler](https://github.com/wso2-extensions/identity-governance/blob/master/components/org.wso2.carbon.identity.password.history/src/main/java/org/wso2/carbon/identity/password/history/handler/PasswordHistoryValidationHandler.java){:target="_blank"} + + - [PasswordPolicyValidationHandler](https://github.com/wso2-extensions/identity-governance/blob/master/components/org.wso2.carbon.identity.password.policy/src/main/java/org/wso2/carbon/identity/password/policy/handler/PasswordPolicyValidationHandler.java){:target="_blank"} + + - [AccountSuspensionNotificationHandler](https://github.com/wso2-extensions/identity-governance/blob/master/components/org.wso2.carbon.identity.account.suspension.notification.task/src/main/java/org/wso2/carbon/identity/account/suspension/notification/task/handler/AccountSuspensionNotificationHandler.java){:target="_blank"} diff --git a/en/includes/references/extend/user-stores/write-a-custom-user-store-manager.md b/en/includes/references/extend/user-stores/write-a-custom-user-store-manager.md new file mode 100644 index 0000000000..0a8da56372 --- /dev/null +++ b/en/includes/references/extend/user-stores/write-a-custom-user-store-manager.md @@ -0,0 +1,572 @@ +# Write a Custom User Store Manager + +The following sections provide information that you need to be aware of when writing a custom user store manager. + +## Available methods and implementations + +There are a set of methods available in the `AbstractUserStoreManager` class. These methods are used when interacting with user stores. When we implement a custom user store manager, it is important to identify the methods that must be implemented or overridden. + +!!! tip "Overriding methods" + + You must select the methods to override based on your requirement. For example, if you want to change the way you encrypt the password, you only need to implement the `preparePassword` method. If you want to implement a completely new read/write user store manager, you need to implement all the methods listed in the tables given below. + If the user store is read-only, you can implement only the important methods and read methods (if you extend from `AbstractUserStoreManager`, you have to keep the unrelated methods empty). + + There are a few other methods used for internal purposes. You do not need to override those methods. + + +The following list briefly explains the use of the available methods in the `AbstractUserStoreManager` class. Most of the methods provide a configuration option through properties. It is recommended to use these methods with the provided customization options. + +### Important methods + +| Available methods | Default behavior | Reason for overriding | +|-------------------|-------------------|--------------------------| +| `boolean doAuthenticate(String userName, Object credential)` | This method returns details on whether the given username and password are matched or not. The credential is usually a string literal. | If you want to change the authentication logic, you can override this method and write your own implementation. The default task of this method is to compare the given password with the stored password. The given credentials are passed to the `preparePassword` method to do the salting or encryption before the comparison takes place. | +| `String preparePassword(Object password, String saltValue)` | This returns the encrypted or plain-text password based on the configurations. | You can override this method if you need to change the way you encrypt the password. If you want to change the algorithm that is used for encryption, you can configure it. | +| `Properties getDefaultUserStoreProperties()` | The default properties of the user store are returned using this method. These properties are used in user store related operations. Be sure to manually add the following property when you implement the class to control whether the user store is enabled or disabled.: `setOptionalProperty("Disabled", "false", "Whether user store is disabled");` | By overriding this method, you can programmatically change the configuration of the user store manager implementation. | +| `boolean checkUserNameValid(String userName)` | Returns whether the given username is compatible with the defined criteria. | This is the criteria used for defining a valid username that can be configured as a regex in user store configurations. If you want to change the way user name validation is done, override this method. | +| `boolean checkUserPasswordValid(Object credential)` | This returns whether the given password is compatible with the defined criteria. This is invoked when creating a user, updating a password, and authorization. | Similar to the username, you can configure the format of a valid password in configuration. If you want to change that behavior, you can override this method. | + + + +### Write methods + + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Available methodsDefault behavior

void doAddUser(String userName, Object credential, String[] roleList, Map<String, String> claims, String profileName, boolean requirePasswordChange)

This method is responsible to create a new user based on the given values. We can change the JDBC query or LDAP attribute name with the user store configuration.

void doDeleteUser(String userName)

This removes the user store record related to the given username.

void doUpdateCredential(String userName, Object newCredential, Object oldCredential)

This is responsible to update the credential of the given username after authenticating with the existing password.

void doUpdateCredentialByAdmin(String userName, Object newCredential)

Admin users can use this method to update the credentials of a given user. This can be done without validating the existing password.

void doAddRole(String roleName, String[] userList, boolean shared)

This creates a new user role with the given roleName and maps the given users to the newly created role. The shared parameter indicates whether this role is shared among tenants or not.

void doDeleteRole(String roleName)

This method removes the given role and related mappings from the user store.

void doUpdateRoleName(String roleName, String newRoleName)

This method is used to update the name of the existing roles.

void doUpdateRoleListOfUser(String userName, String[] deletedRoles, String[] newRoles)

This is used to delete the existing mappings between the given user and deletedRoles while creating mappings to newRoles .

void doUpdateUserListOfRole(String roleName, String[] deletedUsers, String[] newUsers)

This is used to delete the existing mappings between the given role and deletedUsers while creating mappings to newUsers .

void doSetUserClaimValue(String userName, String claimURI, String claimValue, String profileName)

This is responsible for creating a new claim for a given user and profile with the given claim URI and value.

void doSetUserClaimValues(String userName, Map<String, String> claims, String profileName)

This is responsible for creating a new claim for a given user and profile with the given list of claim URIs and values.

void doDeleteUserClaimValue(String userName, String claimURI, String profileName)

Removes the existing claim details mapped with the given user and profile

void doDeleteUserClaimValues(String userName, String[] claims, String profileName)

Removes the given list of claims from a given user

void addRememberMe(String userName, String token)

This method is used to persist tokens in the user store.

Group doAddGroup(String groupName, String groupId, List<String> userIds, Map<String, String>claims)

This method is used to create a group with given parameters.

void doAddGroupWithUserIds(String groupName, List<String> userIds)

This method is used to create a group with given user uuid list.

void doUpdateGroupNameByGroupId(String groupId, String newGroupName)

This method is used to update the name of the group with the given group uuid..

void doUpdateUserListOfGroup(String groupId, List<String> deletedUserIds, List<String> newUserIds)

This method is used to update the user list of a group.

void doDeleteGroupByGroupId(String groupId)

This method is used to delete the group with the given id.

void doAddGroupWithUserNames(String groupName, List<String> userNames)

This method is used to create a group with the given name and given set of user names.

void doUpdateGroupName(String currentGroupName, String newGroupName)

This method is used to update name of the group with the given current name.

void doDeleteGroupByGroupName(String groupName)

This method is used to delete the group with the name

+ +### Read methods + +| Available methods | Default behavior | +|-------------------|-------------------| +| `boolean doCheckExistingUser(String userName)` | Returns whether the provided `userName` already exists in the user store | +| `boolean doCheckExistingRole(String roleName)` | Returns whether the provided `roleName` already exists in the user store | +| `String[] doListUsers(String filter, int maxItemLimit)` | This method returns a list of usernames that match the given filter string. | +| `String[] doGetRoleNames(String filter, int maxItemLimit)` | Returns a list of role names that match with the given filter string. | +| `String[] doGetExternalRoleListOfUser(String userName, String filter)` | Returns a list of external role names of a given user that match with the given filter string | +| `String[] doGetSharedRoleListOfUser(String userName, String tenantDomain, String filter)` | This method returns a list of shared role names of a given user that match the given filter string. | +| `Map getUserPropertyValues(String userName, String[] propertyNames, String profileName)` | This method returns values for the given `propertyNames` for a given `userName` and `profileName`. | +| `String[] getUserListFromProperties(String property, String value, String profileName)` | This returns a list of usernames that match with the given value of the given property and `profileName`. | +| `String[] doGetDisplayNamesForInternalRole(String[] userNames)` | Returns names to display in the UI for given usernames | +| `Date getPasswordExpirationTime(String userName)` | This returns the password expiry date of a given user. The default value is null. | +| `int getUserId(String username)` | This method returns the identifier of a given username. The default value is 0. | +| `boolean doCheckIsUserInRole(String userName, String roleName)` | `True` is returned if the given user is already mapped to the given role name. | +| `String[] getProfileNames(String userName)` | Returns a list of profile names mapped with a given username. | +| `String[] doGetSharedRoleNames(String tenantDomain, String filter, int maxItemLimit)` | This returns a list of role names that are associated with the given tenant domain and match with the filter. | +| `String[] doGetUserListOfRole(String roleName, String filter)` | This method returns a list of usernames that are mapped with the given rolename. | +| `String[] getAllProfileNames()` | All the profile names are returned including the default profile. | +| `boolean isValidRememberMeToken(String userName, String token)` | This method is used to check if the given token exists for the given user. | +| `boolean isMultipleProfilesAllowed()` | This returns whether this user store is allowed to have multiple profiles per user. The default value is `false`. | +| `boolean isBulkImportSupported()` | This method returns whether this user store allows bulk transactions or not. | +| `String doGetGroupIdFromGroupName(String groupName)` | This method returns group uuid which has the given group name. | +| `String doGetGroupNameFromGroupId(String groupId)` | This method returns group name which has the given group uuid. | +| `Group doGetGroupFromGroupName(String groupName, List requiredAttributes)` | This method returns the group with the given group name. | +| `Group doGetGroupFromGroupId(String groupId, List requiredAttributes)` | This method returns the group with the given group uuid. | +| `List doGetGroupListOfUser(String userId, int limit, int offset, String sortBy, String sortOrder)` | This method returns the group list of a given user. | +| `List doListGroups(Condition condition, int limit, int offset, String sortBy, String sortOrder)` | This method return the group list which matches with the given condition. | + + +### Implementations + +In WSO2 Identity Server, there are four user store manager classes that implement the `AbstractUserStoreManager` class. You can select one of those classes according to the user store that you have in your environment. + + ++++ + + + + + + + + + + + + + + + + + + + + + + + + +
userstore manager classWhen you would use it
org.wso2.carbon.user.core.jdbc.UniqueIDJDBCUserStoreManager

If your user details are stored in a database, you must use this user store manager implementation. With the abstraction provided in this implementation, most of the JDBC-based scenarios can be handled without writing a custom user store manager.

org.wso2.carbon.user.core.ldap.UniqueIDReadOnlyLDAPUserStoreManager

You can use this class if you have an LDAP user store. This implementation does not allow you to insert or update users from the WSO2 Identity Server side. Instead, you can only read and use them in the product.

org.wso2.carbon.user.core.ldap.UniqueIDReadWriteLDAPUserStoreManager

If you want to allow WSO2 Identity Server to manipulate user store data, you need to use this implementation.

org.wso2.carbon.user.core.ldap.UniqueIDActiveDirectoryLDAPUserStoreManager

Active Directory also can be used as the user store of WSO2 Identity Server and you can configure it using this user store manager implementation.

+ +--- + +## Implement a custom JDBC user store manager + +The instructions in this section are focused on implementing a sample JDBC user store manager. For this sample, the following tools are used to implement the custom user store manager. + +- Java 1.6.0 +- IDE (Eclipse, InteliJ IDEA, etc.) +- Apache Maven + +--- + +## Set up the implementation + +To set up this implementation, follow the instructions given below. + +1. Create a new Apache Maven project with the help of the IDE that you are using. The project should be a simple Apache Maven project and you can use any desired artifact and group ID. +2. Add the WSO2 user store management .jar file as a dependency on our project. Since this .jar file is stored in WSO2's Maven repository, you need to add the WSO2 repository to your POM file. Given below is a sample POM file. + + !!! note + The version number of the carbon dependencies given below has to be updated according to the carbon kernel version that the particular product version is compatible with. + + ??? example "Sample `pom.xml` file" + ``` xml + + + 4.0.0 + org.wso2.sample + CustomReadOnlyJDBCUserStoreManager + 1.0.0 + bundle + + + wso2-nexus + WSO2 internal Repository + http://maven.wso2.org/nexus/content/groups/wso2-public/ + + true + daily + ignore + + + + + + org.wso2.carbon + org.wso2.carbon.user.core + 4.4.11 + + + org.wso2.carbon + org.wso2.carbon.utils + 4.4.11 + + + org.wso2.carbon + org.wso2.carbon.user.api + 4.4.11 + + + + + + + maven-compiler-plugin + 2.3.1 + true + + UTF-8 + 1.7 + 1.7 + + + + org.apache.felix + maven-scr-plugin + 1.7.2 + + + generate-scr-scrdescriptor + + scr + + + + + + org.apache.felix + maven-bundle-plugin + 2.3.5 + true + + + ${project.artifactId} + ${project.artifactId} + + org.wso2.sample.user.store.manager.internal + + + !org.wso2.sample.user.store.manager.internal, + org.wso2.sample.user.store.manager.*, + + + org.wso2.carbon.*, + org.apache.commons.logging.*, + org.osgi.framework.*, + org.osgi.service.component.* + + + + + + + + ``` + + Now your basic implementation is ready. + +--- + +## Write a custom user store manager for a sample scenario + +Consider a sample scenario where you want to use a custom hashing method using a 3rd party library such as [Jasypt](http://www.jasypt.org/) and to do this, you need to override the `doAuthentication` and `preparePassword` methods. + +Follow the steps given below to write a custom user store manager. + +1. Include the required dependencies in your development environment. To do that, include the relevant Apache Maven dependency details or manually add the `.jar` files to your classpath. For example, add the following XML snippet under the dependencies tag in your `pom.xml` file to include the Jasypt dependency. + + ``` xml + + org.jasypt + jasypt + 1.9.2 + + ``` + +2. Create a new class by extending the existing `JDBCUserStoreManager` implementation. The following code is an example of how this would look. + + ??? example "Code sample" + ``` java + package com.wso2.custom.usermgt; + + + package org.wso2.custom.user.store; + + import org.apache.commons.logging.Log; + import org.apache.commons.logging.LogFactory; + import org.jasypt.util.password.StrongPasswordEncryptor; + import org.wso2.carbon.user.api.RealmConfiguration; + import org.wso2.carbon.user.core.UserCoreConstants; + import org.wso2.carbon.user.core.UserRealm; + import org.wso2.carbon.user.core.UserStoreException; + import org.wso2.carbon.user.core.claim.ClaimManager; + import org.wso2.carbon.user.core.common.AuthenticationResult; + import org.wso2.carbon.user.core.common.FailureReason; + import org.wso2.carbon.user.core.common.User; + import org.wso2.carbon.user.core.jdbc.JDBCRealmConstants; + import org.wso2.carbon.user.core.jdbc.UniqueIDJDBCUserStoreManager; + import org.wso2.carbon.user.core.profile.ProfileConfigurationManager; + import org.wso2.carbon.utils.Secret; + + import java.sql.Connection; + import java.sql.PreparedStatement; + import java.sql.ResultSet; + import java.sql.SQLException; + import java.sql.Timestamp; + import java.util.Date; + import java.util.GregorianCalendar; + import java.util.Map; + + /** + * This class implements the Custom User Store Manager. + */ + public class CustomUserStoreManager extends UniqueIDJDBCUserStoreManager { + + private static final Log log = LogFactory.getLog(CustomUserStoreManager.class); + + private static final StrongPasswordEncryptor passwordEncryptor = new StrongPasswordEncryptor(); + + + public CustomUserStoreManager() { + + } + + public CustomUserStoreManager(RealmConfiguration realmConfig, Map properties, ClaimManager + claimManager, ProfileConfigurationManager profileManager, UserRealm realm, Integer tenantId) + throws UserStoreException { + + super(realmConfig, properties, claimManager, profileManager, realm, tenantId); + log.info("CustomUserStoreManager initialized..."); + } + + + @Override + public AuthenticationResult doAuthenticateWithUserName(String userName, Object credential) + throws UserStoreException { + + boolean isAuthenticated = false; + String userID = null; + User user; + // In order to avoid unnecessary db queries. + if (!isValidUserName(userName)) { + String reason = "Username validation failed."; + if (log.isDebugEnabled()) { + log.debug(reason); + } + return getAuthenticationResult(reason); + } + + if (!isValidCredentials(credential)) { + String reason = "Password validation failed."; + if (log.isDebugEnabled()) { + log.debug(reason); + } + return getAuthenticationResult(reason); + } + + try { + String candidatePassword = String.copyValueOf(((Secret) credential).getChars()); + + Connection dbConnection = null; + ResultSet rs = null; + PreparedStatement prepStmt = null; + String sql = null; + dbConnection = this.getDBConnection(); + dbConnection.setAutoCommit(false); + // get the SQL statement used to select user details + sql = this.realmConfig.getUserStoreProperty(JDBCRealmConstants.SELECT_USER_NAME); + if (log.isDebugEnabled()) { + log.debug(sql); + } + + prepStmt = dbConnection.prepareStatement(sql); + prepStmt.setString(1, userName); + // check whether tenant id is used + if (sql.contains(UserCoreConstants.UM_TENANT_COLUMN)) { + prepStmt.setInt(2, this.tenantId); + } + + rs = prepStmt.executeQuery(); + if (rs.next()) { + userID = rs.getString(1); + String storedPassword = rs.getString(3); + + // check whether password is expired or not + boolean requireChange = rs.getBoolean(5); + Timestamp changedTime = rs.getTimestamp(6); + GregorianCalendar gc = new GregorianCalendar(); + gc.add(GregorianCalendar.HOUR, -24); + Date date = gc.getTime(); + if (!(requireChange && changedTime.before(date))) { + // compare the given password with the stored password using jasypt + isAuthenticated = passwordEncryptor.checkPassword(candidatePassword, storedPassword); + } + } + dbConnection.commit(); + log.info(userName + " is authenticated? " + isAuthenticated); + } catch (SQLException exp) { + try { + this.getDBConnection().rollback(); + } catch (SQLException e1) { + throw new UserStoreException("Transaction rollback connection error occurred while" + + " retrieving user authentication info. Authentication Failure.", e1); + } + log.error("Error occurred while retrieving user authentication info.", exp); + throw new UserStoreException("Authentication Failure"); + } + if (isAuthenticated) { + user = getUser(userID, userName); + AuthenticationResult authenticationResult = new AuthenticationResult( + AuthenticationResult.AuthenticationStatus.SUCCESS); + authenticationResult.setAuthenticatedUser(user); + return authenticationResult; + } else { + AuthenticationResult authenticationResult = new AuthenticationResult( + AuthenticationResult.AuthenticationStatus.FAIL); + authenticationResult.setFailureReason(new FailureReason("Invalid credentials.")); + return authenticationResult; + } + } + + @Override + protected String preparePassword(Object password, String saltValue) throws UserStoreException { + if (password != null) { + String candidatePassword = String.copyValueOf(((Secret) password).getChars()); + // ignore saltValue for the time being + log.info("Generating hash value using jasypt..."); + return passwordEncryptor.encryptPassword(candidatePassword); + } else { + log.error("Password cannot be null"); + throw new UserStoreException("Authentication Failure"); + } + } + + private AuthenticationResult getAuthenticationResult(String reason) { + + AuthenticationResult authenticationResult = new AuthenticationResult( + AuthenticationResult.AuthenticationStatus.FAIL); + authenticationResult.setFailureReason(new FailureReason(reason)); + return authenticationResult; + } + } + + + ``` + +!!! note + The default constructor is not enough when you implement + a custom user store manager and you must implement a constructor + with relevant arguments. + +--- + +## Deploy and configure the custom user store manager + +Follow the instructions given below to deploy and configure the custom user store manager. + +1. Copy the artifact of your project (custom-userstore.jar, in this case) to the `/repository/components/dropins` directory. Also copy all the OSGI bundles to this location. If you have any dependency .jar files, copy them to the `/repository/components/lib` directory. + +2. Add the following configuration to the `/repository/conf/deployment.toml` file to use our custom implementation for user store management. + + Although the existing `UniqueID` user stores are allowed by default, when adding a new user store, note that both existing user stores, as well as new user stores, must be configured as shown below. + + !!! abstract "" + **Format** + ```toml + [user_store_mgt] + custom_user_stores=[""] + ``` + --- + **Sample** + ```toml + [user_store_mgt] + custom_user_stores=["com.wso2.custom.usermgt.CustomUserStoreManager"] + ``` + + !!! tip + This step provides instructions on configuring your custom user store manager as a primary user store. Alternatively, you can configure this as a secondary user store if you already have a different primary user store configured. For more information configuring user stores in WSO2 Identity Server, see [Manage user stores]({{base_path}}/guides/users/user-stores). + + + You do not need to change anything else since you extend the + `JDBCUserStoreManager` class, so the configurations will remain the + same. + +You have now implemented a custom user store manager for WSO2 Identity Server. +Once you have done this, start the product and see the log messages that you have placed inside overridden methods when you create a new user or log in. This ensures that all your configurations work as intended. + +!!! note "Do you want to create a custom user store that only has a few enabled methods?" + 1. Sign in to the WSO2 Identity Server management console (`https://:/carbon`). + 2. Click **User Attributes & Stores** > **User stores** > **New User Store**. + 3. Select the custom user store you just created. + 4. Deselect the **Claim Operations Supported** property. + 5. Click **Finish**. + +## Try out the sample user store manager + +1. Build the sample user store manager [here](https://github.com/wso2/samples-is/tree/master/user-mgt/custom-jdbc-user-store-manager){:target="_blank"} using `mvn clean install` command. +2. Copy the generated `org.wso2.custom.user.store-1.0.0.jar` to the `/repository/components/dropins` directory. +3. Add the following configuration to the `/repository/conf/deployment.toml` file to use our custom implementation for user store management. + ```toml + [user_store_mgt] + custom_user_stores=["org.wso2.custom.user.store.CustomUserStoreManager"] + ``` + +4. Restart the Identity Server. + +!!! note "Note" + Once you add the custom user store manager to the Identity server, you have to configure it as a primary or a secondary user store. + \ No newline at end of file