This example demonstrates how to integrate server side SDK.

Configuring and running the server side SDK

  1. Register an account or login to the Mobile Connect Developer Portal and create an application to obtain your sandbox credentials.
  2. Download the Mobile Connect server side project.

    git clone https://github.com/Mobile-Connect/java_server_side_library.git
  3. Note: if you operate in the EU then you should use EU Discovery Service domain in discovery url: https://eu.discover.mobileconnect.io.

  4. Open the configuration file: [local path]\mobile-connect-demo\src\main\resources\config\OperatorData.json.
    Here are 12 parameters:
    {
      "clientID": your client Id,
      "clientSecret": your client Secret,
      "discoveryURL": your Discovery endpoint,
      "redirectURL": "<protocol>://<hostname>/server_side_api/discovery_callback",
      "xRedirect": "True",
      "includeRequestIP": "False",
      "apiVersion": api version: "mc_v1.1" or "mc_v2.0",
      "scope": scopes,
      "acrValues":  acr_values,
      "MaxDiscoveryCacheSize": max cache size
    }
  5. Build the project. You can configure your application (clientID, clientSecret, discoveryURL, redirectURL). You can also configure your parameters for auth request (xRedirect, includeRequestIP, apiVersion, scope, acrValues). And You can configure cache size (maxDiscoveryCacheSize) and if you want to run or not to run get user info request and get identity request (userInfo, identity).
  6. Download and install any missing dependencies.
  7. Build the server side SDK using Maven repository:
    cd java_server_side_library
    mvn clean package

    Deploy mobile-connect.war.
  8. Prepare client side application (IOS or Android application) or Demo App for Server Side application.

Using the server side Application

The server side SDK for Java automates much of the basic housekeeping and configuration tasks of your application’s integration with Mobile Connect. Many of these functions activate in the Discovery phase, independently of the Identity Gateway endpoint your application will ultimately call, and so are described here for your reference.

Discovery

Discovery is the process of determining an end-user's Mobile Connect Identity Provider, i.e. the Operator ID Gateway. The discovery response will provide with a number of endpoints and the operator specific credentials which will need to use with this ID Gateway.
The server side SDK has a method that accepts user's data as parameters and needs MobileConnectConfig object initialized as part of the MobileConnectWebInterface object - that's enough to make a successful call to the Mobile Connect Discovery endpoint, and the methods attemptDiscovery, attemptDiscoveryAfterOperatorSelection and handleUrlRedirect do just that, building and making the call, and then handling the response. The server side SDK also takes care of caching the response for re-use in a subsequent call – subject to the configured timeout.

Provider Metadata

A successful call to the Mobile Connect Discovery endpoint returns the end user’s Mobile Network Operator (MNO) and describes the Mobile Connect services that MNO supports, via a URI to the MNO’s Provider Metadata. The metadata describes the Identity Gateway endpoints (Mobile Connect services) your application or service can use and how those endpoints are configured – for example, the response types an endpoint can return, the subject identifier types supported, or the Identity Services encryption algorithms in use.
Although Provider Metadata is the primary source of information detailing the Identity Gateway configuration, it does not change often, so a cached version can be used without risk of expired data causing errors. The Mobile Connect server side library handles both the querying of the Provider Metadata and the caching.

  • If the Provider Metadata URI returns no data, the cached metadata is used.
  • Where the cached data is out of date (defaulting to 15-minute intervals) a subsequent query of the URI is attempted, and in the event of a second failed response, expired cached data is used.
  • Should neither the cached data nor the Provider Metadata URI return data (such as an error upon first user login) default values are used.

Regardless of the source, the server side library parses the Provider Metadata into a discrete list of properties. See the OpenID Provider Metadata definition for a list of the metadata available, although you should note that Mobile Connect's implementation may not be exhaustive.
The ProviderMetadata object is available on DiscoveryResponse.ProviderMetadata.

Supported Services

Before your application or service can call an Identity Gateway endpoint (scope), you need to know if the MNO supports the scope you are calling. The server side library provides the method isMobileConnectServiceSupported(String scope) in the discoveryReponse, which can be accessed as follows:

boolean supported = discoveryResponse.isMobileConnectServiceSupported(“mc_authz mc_identity_signup”)

The method accepts a comma- or space-separated list of scopes, which it then checks against the list of supported scopes in the Provider Metadata.

  • If any of the passed scopes are not present in the metadata, the function returns false.
  • If the Provider Metadata is not available, or the scopes attribute is missing, null, or an empty string, the function returns an exception: Provider Metadata scopes unavailable.
  • Version Compatibility

    Although the MNOs may support differing versions of the Mobile Connect APIs, the server side library automatically detects the version in use at an Identity Gateway from the Provider Metadata, and ensures that the correct Mobile Connect Profile is used when making calls to the Mobile Connect APIs.

    Application Short Name

    The application you register with the Mobile Connect Developer Portal includes an Application Short Name, which is stored in the API Exchange and returned in responses from the Discovery service. The server side SDK captures the returned applicationShortName and includes it when constructing calls to the Mobile Connect Authorisation and Identity endpoints.

    Mobile Connect Constants

    The server side SDK provides a number of constants for referencing the Mobile Connect services by scope. They are available at com.gsma.mobileconnect.r2.constants.scopes, and can be called using the following syntax:

    com.gsma.mobileconnect.r2.constants.scopes.MOBILECONNECTIDENTITYPHONE

    The above example calls the scope "openid mc_identity_phonenumber". You can pass multiple scopes as a space-separated string; the server side library will remove any duplicates before making the call to the Identity Gateway.

    The following constants are available:

    Mobile Connect Product Constant Identifier Literal Value
    Authentication (v1.0) MOBILECONNECT "openid"
    Authentication (v1.1) MOBILECONNECTAUTHENTICATION "openid mc_authn"
    Authorisation MOBILECONNECTAUTHORIZATION "openid mc_authz"
    Identity: Phone Number MOBILECONNECTIDENTITYPHONE "openid mc_identity_phonenumber"
    Identity: Signup MOBILECONNECTIDENTITYSIGNUP "openid mc_identity_signup"
    Identity: Signup Plus* MOBILECONNECTIDENTITYSIGNUPPLUS "openid mc_identity_signupplus"
    Identity: National ID MOBILECONNECTIDENTITYNATIONALID "openid mc_identity_nationalid"

    *This constant is available to support possible future products.

    Login Hint Support

    You have the option to provide the login hint to the Identity Gateway using one of three formats: MSISDN, encrypted MSISDN, and PCR. Your decision on how to provide the login hint is governed by two factors:

    • The login hint formats supported by the Identity Gateway.
    • Whether you are a “Trusted Service Provider”; an unencrypted MSISDN is only accepted from a trusted provider – attempting to send one if you are not trusted returns an error.

    The Mobile Connect server side library provides functions to test for login hint support in the Identity Gateway, namely:

    • isSupportedForMSISDN
    • isSupportedForEncryptedMSISDN
    • isSupportedForPCR

    Each function checks the login_hint_methods_supported attribute in the Provider Metadata returned from the Discovery endpoint. Should the attribute be missing, the function checks the Mobile Connect Profile version supported; only version 1.2 supports passing the PCR as the login hint.

    Once you have decided how to provide the login hint, the server side library offers a further three functions to build it for you:

    • generateForMSISDN (String MSISDN)
    • generateForEncryptedMSISDN (String encryptedMSISDN)
    • generateForPCR (String PCR)

    id_token Validation

    A successful response from the Identity Gateway includes an id_token – a JSON Web Token, which validates against a JSON Web Keyset (JWKS), available at a URL specified in the Provider Metadata attribute jwks_uri.

    The server side SDK performs a number of automatic validation actions to ensure the integrity of the response source, such as checking whether the token has expired. It also fetches the data from the jwks_uri location and stores it alongside the associated Discovery response, where it is cached. The following functions are then available to you to support id_token validation:

    • validateIdTokenSignature(id_token) verifies the signature of the id_token based on the jwks_uri data. A successful validation returns true; a failed validation returns false; a missing JWKS certificate returns an error.
    • retrieveJWKS allows you to fetch all of the keys from the JWKS data.
    • returnMatchingJWKSEntries allows you to fetch specific keys matching the following parameters:

      • kty – key type (e.g. RSA)
      • alg – algorithm (e.g. RS256)
      • use – sig (signature) or enc (encryption)
      • kid – key identifier

    Using Mobile Connect Authentication

    The server side SDK allows you to call a method that accepts a reference to a configuration file as a parameter. With your developer credentials specified in the configuration file, you have everything you need to make a successful call to the Mobile Connect Discovery endpoint, and the methods attemptDiscovery, attemptDiscoveryAfterOperatorSelection, and handleUrlRedirect do just that, building and making the call, and then handling the response. The server side SDK also takes care of caching the response for re-use in a subsequent call – subject to the configured timeout. Once the end user has entered their MSISDN or specified their operator, and a successful Discovery response is received, the server side library builds the call to the Authentication endpoint. A successful authentication returns an id_token containing the user’s PCR, which is unpacked and available for verification against your user register.

    Server side Application

    With your configuration specified in the configuration file you have everything you need to make a successful call to the server side application.

    Client side application allows to sends a request to server side application in three modes: msisdn, mcc_mnc, none

    Example request:
    If you are using only msisdn the request to your server side part will be: http://localhost:8080/server_side_api/start_discovery?msisdn=447700900907
    When you are using mcc and mnc it will be: http://localhost:8080/server_side_api/start_discovery?mcc=907&mnc=07
    If you choose None mode: http://localhost:8080/server_side_api/start_discovery
    Also sourceIp can be included to request: http://localhost:8080/server_side_api/start_discovery?sourceIp=10.0.0.7

    Client side application sends a GET request with client data to server side application. Then server side application makes startDiscovery method call with your data from configuration file. To interact with Discovery API is used attemptDiscovery method with your parameters from config file and client data. And then startAuthentication method perfoms for getting authentication URL. Server side application sends this URL to client side application.

    public ResponseEntity startDiscovery(
                @RequestParam(required = false) final String msisdn,
                @RequestParam(required = false) final String mcc,
                @RequestParam(required = false) final String mnc,
                @RequestParam(required = false) final String sourceIp,
                final HttpServletRequest request)
        {
            this.getParameters();
            String sourceIpLocal = sourceIp;
    
            if (sourceIp == null)
            {
                sourceIpLocal = request.getHeader("X-Source-IP") == null ? null : request.getHeader("X-Source-IP");
            }
            MobileConnectRequestOptions requestOptions =
                    new MobileConnectRequestOptions.Builder()
                            .withDiscoveryOptions(new DiscoveryOptions.Builder()
                                    .withClientIp(sourceIpLocal).build())
                            .build();
    
            MobileConnectStatus status =
                    this.mobileConnectWebInterface.attemptDiscovery(request, msisdn, mcc, mnc, true, mobileConnectConfig.getIncludeRequestIp(), requestOptions);
    
            if (handleErrorMsg(status)) {
                requestOptions =
                        new MobileConnectRequestOptions.Builder()
                                .withDiscoveryOptions(new DiscoveryOptions.Builder().build())
                                .build();
                status = this.mobileConnectWebInterface.attemptDiscovery(request, null, null, null, false, includeRequestIP, requestOptions);
                sourceIpLocal = null;
            }
    
            if (status.getDiscoveryResponse() != null && status.getDiscoveryResponse().getResponseCode() == Constants.ResponseOk)
            {
                setCacheByRequest(msisdn, mcc, mnc, sourceIpLocal, status.getDiscoveryResponse());
    
                String url = startAuthentication(
                        status.getSdkSession(),
                        status.getDiscoveryResponse().getResponseData().getSubscriberId(),
                        request,
                        msisdn, mcc, mnc, sourceIpLocal);
    
                HttpHeaders headers = new HttpHeaders();
                headers.add("Location", url);
                return ResponseEntity.status(HttpStatus.FOUND).headers(headers).build();
            }
    
            else {
                HttpHeaders headers = new HttpHeaders();
                headers.add("Location", status.getUrl());
                return ResponseEntity.status(HttpStatus.FOUND).headers(headers).build();
            }
        }

    To generate the request parameters it uses getParameters method:
    private void getParameters() {
        operatorParams = ReadAndParseFiles.readFile(Constants.ConfigFilePath);
        apiVersion = operatorParams.getApiVersion();
        includeRequestIP = operatorParams.getIncludeRequestIP().equals("True");
        this.cache = new ConcurrentCache.Builder().withJsonService(this.jsonService).build();
        try {
            mobileConnectConfig = new MobileConnectConfig.Builder()
                    .withClientId(operatorParams.getClientID())
                    .withClientSecret(operatorParams.getClientSecret())
                    .withDiscoveryUrl(new URI(operatorParams.getDiscoveryURL()))
                    .withRedirectUrl(new URI(operatorParams.getRedirectURL()))
                    .withXRedirect(operatorParams.getXRedirect().equals("True") ? "APP" : "False")
                    .build();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
    }

    1) If you selected the msisdn mode: stateDiscoveryCallback method is called.

    @GetMapping(value = "discovery_callback")
        @ResponseBody
        @ResponseStatus(HttpStatus.FOUND)
        public String stateDiscoveryCallback(@RequestParam(required = false) String state,
                                             @RequestParam(required = false) final String error,
                                             @RequestParam(required = false) final String description,
                                             @RequestParam(required = false) final String error_description,
                                             final HttpServletRequest request)
        {
            if (error != null)
            {
                String errorDescription = description;
                if (description==null) {
                    errorDescription = error_description;
                }
                return new Gson().toJson(new MobileConnectWebResponse(MobileConnectStatus.error(error, errorDescription, new Exception())));
            }
            final MobileConnectRequestOptions options = new MobileConnectRequestOptions.Builder()
                    .withAuthenticationOptions(new AuthenticationOptions.Builder()
                            .withContext(apiVersion.equals(Constants.Version2) ? Constants.ContextBindingMsg : null)
                            .withBindingMessage(apiVersion.equals(Constants.Version2) ? Constants.ContextBindingMsg : null)
                            .withClientName(clientName)
                            .build())
                    .build();
    
            URI requestUri = HttpUtils.extractCompleteUrl(request);
            MobileConnectStatus authConnectStatus = this.mobileConnectWebInterface.handleUrlRedirect(request, requestUri, discoveryCache.get(state), state, cachedParameters.getNonce(), options);
    
            if (handleErrorMsg(authConnectStatus))
            {
                removeSessionFromCache(discoveryCache, options.getDiscoveryOptions());
                return new Gson().toJson(new MobileConnectWebResponse(MobileConnectStatus.error(error, description, new Exception())));
            }
    
            cachedParameters.setAccessToken(authConnectStatus.getRequestTokenResponse().getResponseData().getAccessToken());
    
            if (operatorParams.getIdentity().equals("True")) {
                MobileConnectWebResponse identityResp = requestIdentity(state, authConnectStatus.getRequestTokenResponse().getResponseData().getAccessToken(), request);
                return createIdentityResponse(authConnectStatus, identityResp);
            }
    
            if (operatorParams.getUserInfo().equals("True")) {
                MobileConnectWebResponse userInfoResp = requestUserInfo(state, authConnectStatus.getRequestTokenResponse().getResponseData().getAccessToken(), request);
                return createIdentityResponse(authConnectStatus, userInfoResp);
            }
            return createIdentityResponse(authConnectStatus, null);
        }

    This method calles handleUrlRedirect (where a token will receive) and also requestUserInfo or requestIdentity if it was specified in the configuration.

     static MobileConnectStatus handleUrlRedirect(final IDiscoveryService discoveryService, 
            final IJWKeysetService jwKeysetService, final IAuthenticationService authnService,
            final URI redirectedUrl, final DiscoveryResponse discoveryResponse,
            final String expectedState, final String expectedNonce, final MobileConnectConfig config,
            final MobileConnectRequestOptions options, final IJsonService jsonService,
            final IMobileConnectEncodeDecoder iMobileConnectEncodeDecoder)
        {
            ObjectUtils.requireNonNull(redirectedUrl, "redirectedUrl");
    
            if (HttpUtils.extractQueryValue(redirectedUrl, Parameters.CODE) != null)
            {
                MobileConnectStatus tokenStatus = requestToken(authnService, jwKeysetService, discoveryResponse, redirectedUrl,
                        expectedState, expectedNonce, config, options, jsonService,
                        iMobileConnectEncodeDecoder);
                return tokenStatus;
    
            }
            else  if (redirectedUrl.toString().contains(Parameters.DISCOVERY_CALLBACK))
            {       
                MobileConnectStatus tokenStatus = requestToken(authnService, jwKeysetService, discoveryResponse, redirectedUrl,
                        expectedState, expectedNonce, config, options, jsonService,
                        iMobileConnectEncodeDecoder);
                return tokenStatus;
            }
            else if (HttpUtils.extractQueryValue(redirectedUrl, Parameters.MCC_MNC) != null)
            {           
                return attemptDiscoveryAfterOperatorSelection(discoveryService, redirectedUrl, config);
            }
            else
            {
                final String errorCode = HttpUtils.extractQueryValue(redirectedUrl, Parameters.ERROR);
                String errorDescription =
                    HttpUtils.extractQueryValue(redirectedUrl, Parameters.ERROR_DESCRIPTION);
    
                if (errorDescription == null)
                {
                    errorDescription =
                        HttpUtils.extractQueryValue(redirectedUrl, Parameters.DESCRIPTION);
                }
    
                final MobileConnectStatus status =
                    MobileConnectStatus.error(ObjectUtils.defaultIfNull(errorCode, "invalid_request"),
                        ObjectUtils.defaultIfNull(errorDescription,
                            String.format("unable to parse next step using %s", redirectedUrl)), null);          
                return status;
            }
        }

    2) mcc_mnc mode.
    3) none mode.

    If you selected the mcc_mnc mode or none mode you will be redirected to the operator selection page. Here you can type your msisdn and process page.
    Then mccMncDiscoverycallback method is called to enter phone number.

        @GetMapping(value = "discovery_callback", params = "mcc_mnc")
        @ResponseBody
        @ResponseStatus(HttpStatus.FOUND)
        public RedirectView mccMncDiscoverycallback(@RequestParam(required = false) final String mcc_mnc,
                                                    @RequestParam(required = false) final String subscriber_id,
                                                    final HttpServletRequest request)
        {
    
            final MobileConnectRequestOptions options = new MobileConnectRequestOptions.Builder()
                    .withAuthenticationOptions(new AuthenticationOptions.Builder()
                            .withContext(apiVersion.equals(Constants.Version2) ? Constants.ContextBindingMsg : null)
                            .withBindingMessage(apiVersion.equals(Constants.Version2) ? Constants.ContextBindingMsg : null)
                            .withClientName(clientName)
                            .build())
                    .build();
    
            Map<String, String> mccMnc = parseMccMnc(mcc_mnc);
            MobileConnectStatus status = this.mobileConnectWebInterface.attemptDiscovery(request, null, mccMnc.get("mcc"), mccMnc.get("mnc"), true, mobileConnectConfig.getIncludeRequestIp(), options);
    
            if (status.getDiscoveryResponse() != null) {
                setCacheByRequest(null, mccMnc.get("mcc"), mccMnc.get("mnc"), null, status.getDiscoveryResponse());
                String url = startAuthentication(
                        status.getSdkSession(),
                        subscriber_id,
                        request, null, mccMnc.get("mcc"), mccMnc.get("mnc"), null);
                return new RedirectView(url);
            }
            else {
                return new RedirectView(status.getUrl(), true);
            }
        }

    Discovery caching

    Discovery response is cached with following rules:
    1) After succeed Discovery request, Discovery response is cached with used mcc/mnc, msisdn or sourceIp.
    2) Expiration time for Discovery Response cache is the same as "ttl" field in Discovery Response - if Discovery Response expired - that deleted from cache
    3) Next requests for Discovery with the same mcc/mnc, msisdn or sourceIp - Discovery request shouldn't be performed, Discovery response should be taken from cache
    4) If auth call respond with error - Discovery Response deleted from cache, and you are redirected to Discovery UI

    Using Mobile Connect Authorisation

    The server side SDK allows you to call the Identity Gateway with the scope parameter set to “mc_authz”, which signifies an authorisation request for a single transaction (the id_token and access token returned from the Gateway have a timeout set to zero, so expire after a single use). You should check that the Identity Gateway supports version 1.2 of the Mobile Connect Profile (available in the Provider Metadata) before attempting a call, but note that the server side library sets the version to “mc_v1.2” automatically.

    To make a successful authorisation call, you must provide the following additional parameters:

    • client_name – specifies the name of the application/service requesting authorisation. This value is taken from Options and must match the Application Short Name.
    • context – specifies the reason for the authorisation request, and should be built from the data describing the transaction requiring authorisation. The context is displayed on the authenticating (mobile) device only.
    • binding_message– specifies a reference string to display on the device from which the authorisation request was invoked, and on the authenticating (mobile) device, allowing the user to visually verify that the confirmation message originated from their transaction request.

    Note: the authorisation prompt displayed to the user combines all three parameters, which cannot exceed 93 bytes in total.

    The following example shows how to add the additional options to the authentication call described in Using Mobile Connect Authentication, resulting in a correctly configured call to the authorisation service.

    public MobileConnectWebResponse startAuthentication()
    {
        …
    
        final MobileConnectRequestOptions options = new MobileConnectRequestOptions.Builder()
            .withAuthenticationOptions(new AuthenticationOptions.Builder()
                .withScope(com.gsma.mobileconnect.r2.constants.Scopes.MOBILECONNECTAUTHORIZATION)
                .withContext("demo")
                .withBindingMessage("demo auth")
                .build())
            .build();
    
        …
    }

    startAuthentication method generates URL for authorisation, which is sent to client side application.

    @GetMapping({"start_authentication", "start_authorization"})
        @ResponseBody
        @ResponseStatus(HttpStatus.FOUND)
        public String startAuthentication(
                @RequestParam(required = false) final String sdkSession,
                @RequestParam(required = false) final String subscriberId, final HttpServletRequest request, final String msisdn, final String mcc, final String mnc, final String sourceIp)
        {       
            String scope = operatorParams.getScope();
            if (scope == null && operatorUrls.getProviderMetadataUri() == null) {
                apiVersion = Constants.Version1;
            } else if (scope == null){
                apiVersion = Constants.Version2;
            }
    
            final MobileConnectRequestOptions options = new MobileConnectRequestOptions.Builder()
                    .withAuthenticationOptions(new AuthenticationOptions.Builder()
                            .withScope(scope)
                            .withContext(apiVersion.equals(Constants.Version2) ? Constants.ContextBindingMsg : null)
                            .withBindingMessage(apiVersion.equals(Constants.Version2) ? Constants.ContextBindingMsg : null)
                            .withClientName(clientName)
                            .build())
                    .build();
            final MobileConnectStatus status =
                    this.mobileConnectWebInterface.startAuthentication(request, sdkSession, subscriberId,
                            null, null, options);
    
            if (status.getErrorMessage() != null) {
    
                return String.format("Authentication was failed: '{0}' , with code: {1}", status.getErrorMessage(), status.getErrorCode());
            }
            cachedParameters.setNonce(status.getNonce());
            if (msisdn != null) {
                try {
                    discoveryCache.add(status.getState(), discoveryCache.get(msisdn));
                } catch (CacheAccessException e) {
                    e.printStackTrace();
                }
            }
    
            if (mcc != null) {
                try {
                    discoveryCache.add(status.getState(), discoveryCache.get(String.format("%s_%s", mcc, mnc)));
                } catch (CacheAccessException e) {
                    e.printStackTrace();
                }
            }
    
            if (sourceIp != null) {
                try {
                    discoveryCache.add(status.getState(), discoveryCache.get(sourceIp));
                } catch (CacheAccessException e) {
                    e.printStackTrace();
                }
            }
    
            return status.getUrl();
        }

    After that client side makes redirects session and gets the code. Client side sends this code to server side, which uses this code for requestToken method performing.

    static MobileConnectStatus requestToken(final IAuthenticationService authnService,
            final IJWKeysetService jwKeysetService, final DiscoveryResponse discoveryResponse,
            final URI redirectedUrl, final String expectedState, final String expectedNonce,
            final MobileConnectConfig config, final MobileConnectRequestOptions options,
            final IJsonService jsonService,
            final IMobileConnectEncodeDecoder iMobileConnectEncodeDecoder)
        {
            ObjectUtils.requireNonNull(discoveryResponse, DISCOVERY_RESPONSE);
            StringUtils.requireNonEmpty(expectedState, "expectedState");
    
            long maxAge = extractMaxAge(options);
    
            if (!isUsableDiscoveryResponse(discoveryResponse))
            {
                return MobileConnectStatus.startDiscovery();
            }
    
            final String actualState = HttpUtils.extractQueryValue(redirectedUrl, "state");
            if (!expectedState.equals(actualState))
            {           
                return MobileConnectStatus.error("invalid_state",
                    "state values do not match, possible cross-site request forgery", null);
            }
            else
            {
                final String code = HttpUtils.extractQueryValue(redirectedUrl, "code");
                final String clientId = ObjectUtils.defaultIfNull(
                    discoveryResponse.getResponseData().getResponse().getClientId(),
                    config.getClientId());
                final String clientSecret = ObjectUtils.defaultIfNull(
                    discoveryResponse.getResponseData().getResponse().getClientSecret(),
                    config.getClientSecret());
                final String correlationId =
                        discoveryResponse.getResponseData().getCorrelationId();
                final String requestTokenUrl = discoveryResponse.getOperatorUrls().getRequestTokenUrl();
                final String issuer = discoveryResponse.getProviderMetadata().getIssuer();
    
                try
                {
                    final Future<RequestTokenResponse> requestTokenResponseFuture =
                        authnService.requestTokenAsync(clientId, clientSecret, correlationId,
                            URI.create(requestTokenUrl), config.getRedirectUrl(), code);
    
                    final RequestTokenResponse requestTokenResponse = requestTokenResponseFuture.get();
    
                    MobileConnectStatus status = processRequestTokenResponse(requestTokenResponse, expectedState,
                            expectedNonce, redirectedUrl, iMobileConnectEncodeDecoder, jwKeysetService,
                            discoveryResponse, clientId, issuer, maxAge, jsonService,
                            discoveryResponse.getProviderMetadata().getVersion());
                    return status;
                }
                catch (final Exception e)
                {
                    return MobileConnectStatus.error("request token", e);
                }
            }
        }

    If userInfo or identity parameter has True value in configuration file , requestUserInfo or requestIdentity respectively will be performed and the JSON response will be returned to client side application. Else client side application will receive the Token JSON response.

    Using Mobile Connect Identity and Attributes

    A successful call to the Authorisation endpoint returns an id_token identifying the user, and an access token that grants your application permission to request their personal information (referred to as Claims). This information contains a range of data; the exact data you can request is specified in the access token, and is limited to those Claims the end user has permitted as part of the original authorisation request.

    Note: the access token is for single use only, and expires immediately upon use.

    You request access to the Identity endpoint by specifying the appropriate scope. The server side SDK provides constants that you can pass when requesting specific Identity products:

    • Identity: Phone Number – MOBILECONNECTIDENTITYPHONE
    • Identity: Sign-up – MOBILECONNECTIDENTITYSIGNUP
    • Identity: National Identity – MOBILECONNECTIDENTITYNATIONALID

    Upon successful authorisation, the server side SDK provides an IdentityResponse with the user information JSON available as a property. The following example can be used to convert the JSON data to a class - IdentityData - which is provided with all recognised claims.

    final MobileConnectStatus response =
                this.mobileConnectWebInterface.requestIdentity(request, discoveryResponse, accessToken);
    
    final IdentityResponse identityResponse = response.getIdentityResponse();

    The following example shows how to add the additional Authorisation and Identity options to the Authentication call described in Using Mobile Connect Authentication, resulting in a correctly configured call to the Identity: Phone Number service.

    Note: calls to Identity: Sign-up and Identity: National ID are structured in exactly the same way, but using the scopes “mc_identity_signup” and “mc_identity_nationalid” as applicable.

    Add the MobileConnectRequestOptions required for Authorisation and Identity: Phone Number.

    public MobileConnectWebResponse startAuthentication()
    {
    	…
            final MobileConnectRequestOptions options = new MobileConnectRequestOptions.Builder()
                .withAuthenticationOptions(new AuthenticationOptions.Builder()
                    .withScope(“openid mc_authz mc_identity_phonenumber”)
                    .withContext("Demo Context")
                    .withBindingMessage("Demo Message")
                    .build())
                .build();
    
            …
        }

    Add the requestIdentity() and requestUserInfo() methods to request the identity data following a successful authorisation.

    public MobileConnectWebResponse requestIdentity(
                @RequestParam(required = false) final String state,
                @RequestParam(required = false) final String accessToken, final HttpServletRequest request)
        {
            final MobileConnectStatus status =
                    this.mobileConnectWebInterface.requestIdentity(request, discoveryCache.get(state), accessToken == null ? cachedParameters.getAccessToken() : accessToken);
    
            return new MobileConnectWebResponse(status);
        } 

     public MobileConnectWebResponse requestUserInfo(
                @RequestParam(required = false) final String state,
                @RequestParam(required = false) final String accessToken, final HttpServletRequest request)
        {      
            final MobileConnectStatus status =
                    this.mobileConnectWebInterface.requestUserInfo(request, discoveryCache.get(state), accessToken == null ? cachedParameters.getAccessToken() : accessToken);
    
            return new MobileConnectWebResponse(status);
        }