This pages shows you how to setup Java Demo (Server Side SDK) App using discovery and without discovery.
Content:
Configuring and running the server side SDK in With Discovery Mode
- Register an account or login to the Mobile Connect Developer Portal and create an application to obtain your sandbox credentials.
-
Download the Mobile Connect server side project.
git clone https://github.com/Mobile-Connect/java_server_side_library.git
- 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, "clientName": your client Name, "discoveryURL": your Discovery endpoint, "redirectURL": "<protocol>://<hostname>/server_side_api/discovery_callback", "xRedirect": "True", "includeRequestIP": "False", "apiVersion": api version, "scope": scope, "acrValues": acr_values, "MaxDiscoveryCacheSize": max cache size, "loginHintTokenPreference": "True" }
-
Open sector_identifier_uri.json file and specify the value of sector_identifier_uri with a single JSON array of redirect_uri values.
["<protocol>://<hostname>/server_side_api/discovery_callback"]
- 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). 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). And you can set preference (loginHintTokenprefetence) to use login_hint_token parameter except login_hint.
- Build Demo App using Maven repository:
cd java_server_side_library mvn clean package
- Deploy mobile-connect.war.
- Prepare client side application (IOS or Android application) or Demo App for Server Side application.
Note: if you operate in the EU then you should use EU Discovery Service domain in discovery url: https://eu.discover.mobileconnect.io
.
Using Mobile Connect Server Side SDK with Discovery
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 ip
can be included to request: as query param sourceIp http://localhost:8080/server_side_api/start_discovery?sourceIp=10.0.0.7
or as value of X-Forwarded-For header.
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 RedirectView startDiscovery(
@RequestParam(required = false) final String msisdn,
@RequestParam(required = false) final String mcc,
@RequestParam(required = false) final String mnc,
@RequestParam(required = false) String sourceIp,
@RequestParam(required = false) boolean ignoreIp,
final HttpServletRequest request)
{
LOGGER.info("* Attempting discovery for msisdn={}, mcc={}, mnc={}, sourceIp={}",
LogUtils.mask(msisdn, LOGGER, Level.INFO), mcc, mnc, sourceIp);
this.mobileConnectWebInterface = MobileConnect.buildWebInterface(mobileConnectConfig, new DefaultEncodeDecoder(), this.sessionCache, this.discoveryCache);
this.getParameters();
if (StringUtils.isNullOrEmpty(sourceIp) & !ignoreIp) {
sourceIp = includeRequestIP ? HttpUtils.extractClientIp(request) : null;
}
DiscoveryResponse discoveryResponse = getDiscoveryCache(msisdn, mcc, mnc, sourceIp);
MobileConnectStatus status;
if (discoveryResponse == null) {
status = attemptDiscovery(msisdn, mcc, mnc, sourceIp, request);
discoveryResponse = status.getDiscoveryResponse();
if (discoveryResponse == null || discoveryResponse.getResponseCode() !=
org.apache.http.HttpStatus.SC_OK) {
if (status.getUrl() != null) {
return new RedirectView(status.getUrl(), true);
}
else {
return startDiscovery(null, null, null, null, true, request);
}
}
}
setDiscoveryCache(msisdn, mcc, mnc, sourceIp, discoveryResponse);
String url;
if (operatorParams.getScope().contains(Scope.AUTHZ)) {
url = startAuthorize(
discoveryResponse,
discoveryResponse.getResponseData().getSubscriberId(),
request,
msisdn, mcc, mnc, sourceIp);
} else {
url = startAuthentication(
discoveryResponse,
discoveryResponse.getResponseData().getSubscriberId(),
request,
msisdn, mcc, mnc, sourceIp);
}
if (url == null) {
return startDiscovery(null, null, null, null, true, request);
}
return new RedirectView(url);
}
To generate the request parameters it uses getParameters
method:
private void getParameters() {
operatorParams = ReadAndParseFiles.readFile(Constants.CONFIG_FILE_PATH);
if(operatorParams == null) {
operatorParams = ReadAndParseFiles.readFile(Constants.CONFIG_FILE_PATH.replace("file:/", ""));
}
if(operatorParams == null) {
operatorParams = ReadAndParseFiles.readFile(Constants.CONFIG_FILE_PATH.replace("file:", ""));
}
apiVersion = operatorParams.getApiVersion();
includeRequestIP = operatorParams.getIncludeRequestIP().equals("True");
loginHintTokenPreference = operatorParams.getLoginHintTokenPreference().equals("True");
if (!apiVersion.equals(DefaultOptions.MC_V3_0)) {
loginHintTokenPreference = false;
}
sessionCache = new SessionCache.Builder()
.withJsonService(this.jsonService)
.withMaxCacheSize(operatorParams.getMaxDiscoveryCacheSize())
.build();
clientName = operatorParams.getClientName();
discoveryCache = new DiscoveryCache.Builder().withJsonService(this.jsonService).withMaxCacheSize(operatorParams.getMaxDiscoveryCacheSize()).build();
try {
mobileConnectConfig = new MobileConnectConfig.Builder()
.withClientId(operatorParams.getClientID())
.withClientSecret(operatorParams.getClientSecret())
.withClientName(operatorParams.getClientName())
.withDiscoveryUrl(new URI(operatorParams.getDiscoveryURL()))
.withRedirectUrl(new URI(operatorParams.getRedirectURL()))
.withXRedirect(operatorParams.getXRedirect().equals("True") ? "APP" : "False")
.withIncludeRequestIP(includeRequestIP)
.build();
} catch (URISyntaxException e) {
LOGGER.error("Wrong URI provided");
}
}
1) If you selected the msisdn
mode: stateDiscoveryCallback
method is called.
public ModelAndView stateDiscoveryCallback(@RequestParam(required = false) String state,
@RequestParam(required = false) final String error,
@RequestParam(required = false) final String error_description,
@RequestParam(required = false) final String description,
final HttpServletRequest request)
{
String operationStatus;
if (error != null)
{
if (operatorParams.getScope().contains(Scope.AUTHN) || operatorParams.getScope().equals(Scope.OPENID)) {
operationStatus = Status.AUTHENTICATION;
} else {
operationStatus = Status.AUTHORISATION;
}
return redirectToView(MobileConnectStatus.error(error,
ObjectUtils.defaultIfNull(description, error_description), new Exception()), operationStatus);
}
final MobileConnectRequestOptions options = new MobileConnectRequestOptions.Builder()
.withAuthenticationOptions(new AuthenticationOptions.Builder()
.withContext((apiVersion.equals(Constants.VERSION_2_0) || apiVersion.equals(Constants.VERSION_2_3) || apiVersion.equals(Constants.VERSION_3_0)) ? Constants.CONTEXT_BINDING_MSG : null)
.withBindingMessage((apiVersion.equals(Constants.VERSION_2_0) || apiVersion.equals(Constants.VERSION_2_3) || apiVersion.equals(Constants.VERSION_3_0)) ? Constants.CONTEXT_BINDING_MSG : null)
.withClientName(clientName)
.build())
.build();
URI requestUri = HttpUtils.extractCompleteUrl(request);
SessionData sessionData = sessionCache.get(state);
MobileConnectStatus status = this.mobileConnectWebInterface.handleUrlRedirect(request, requestUri,
sessionData.getDiscoveryResponse(), state, sessionData.getNonce(), options, apiVersion, true);
if (apiVersion.equals(DefaultOptions.VERSION_MOBILECONNECT) && !StringUtils.isNullOrEmpty(sessionData.getDiscoveryResponse().getOperatorUrls().getUserInfoUrl())) {
for (String userInfoScope : userinfoScopes) {
if (operatorParams.getScope().contains(userInfoScope)) {
final MobileConnectStatus statusUserInfo =
this.mobileConnectWebInterface.requestUserInfo(request, sessionData.getDiscoveryResponse(),
status.getRequestTokenResponse().getResponseData().getAccessToken());
status = status.withIdentityResponse(statusUserInfo.getIdentityResponse());
break;
}
}
} else if ((apiVersion.equals(DefaultOptions.MC_V3_0) || apiVersion.equals(DefaultOptions.MC_V2_3) || apiVersion.equals(DefaultOptions.MC_V2_0))
&& !StringUtils.isNullOrEmpty(sessionData.getDiscoveryResponse().getOperatorUrls().getPremiumInfoUri())) {
for (String identityScope : identityScopes) {
if (operatorParams.getScope().contains(identityScope)) {
final MobileConnectStatus statusIdentity =
this.mobileConnectWebInterface.requestIdentity(request, sessionData.getDiscoveryResponse(),
status.getRequestTokenResponse().getResponseData().getAccessToken());
status = status.withIdentityResponse(statusIdentity.getIdentityResponse());
break;
}
}
} else {
return redirectToView(status, Status.TOKEN);
}
return redirectToView(status, Status.PREMIUMINFO);
}
}
This method calles
handleUrlRedirect
(where a token will receive) and also requestUserInfo
or requestIdentity
if it was specified in the configuration.
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.
final MobileConnectRequestOptions options = new MobileConnectRequestOptions.Builder()
.withAuthenticationOptions(new AuthenticationOptions.Builder()
.withContext((apiVersion.equals(Constants.VERSION_2_0) || apiVersion.equals(Constants.VERSION_2_3) || apiVersion.equals(Constants.VERSION_3_0)) ? Constants.CONTEXT_BINDING_MSG : null)
.withBindingMessage((apiVersion.equals(Constants.VERSION_2_0) || apiVersion.equals(Constants.VERSION_2_3) || apiVersion.equals(Constants.VERSION_3_0)) ? Constants.CONTEXT_BINDING_MSG : null)
.withClientName(clientName)
.build())
.build();
String[] mccMncArray = mcc_mnc.split("_");
String mcc = mccMncArray[0];
String mnc = mccMncArray[1];
MobileConnectStatus status = this.mobileConnectWebInterface.attemptDiscovery(request, null, mcc, mnc, true, mobileConnectConfig.getIncludeRequestIp(), options);
if (status.getDiscoveryResponse() != null) {
setDiscoveryCache(null, mcc, mnc, null, status.getDiscoveryResponse());
String url;
if (operatorParams.getScope().contains(Scope.AUTHZ)) {
url = startAuthorize(
status.getDiscoveryResponse(),
subscriber_id,
request, null, mcc, mnc, null);
} else {
url = startAuthentication(
status.getDiscoveryResponse(),
subscriber_id,
request, null, mcc, mnc, null);
}
return new RedirectView(url);
}
else {
return new RedirectView(status.getUrl(), true);
}
Discovery caching
Demo Server Side SDK records each successful Discovery Response in Discovery Cache.
Adding, getting and removing records is performing by using a key that includes user MSISDN, MCC, MNC, IP.
Record is removing from cache only in case of expired Discovery Response. Response expiration is determined by “ttl” (time to live) parameter value processing in Discovery Response.
The ‘ttl’ value should be in the following range:
In case of ‘ttl’ value is less than 5mins, Discovery Response’s ‘ttl’ is overwritten with 5mins.
In case of ‘ttl’ value is more than 259200mins, Discovery Response’s ‘ttl’ is overwritten with 259200mins.
There no cache autoremove mechanism or clearing cache based on the Authentication/Authorisation error response. Removing from cache can occur only during an attempt to get a record. Discovery Cache receives request for a record with particular key. Inner mechanism search record in storage and performs verification. If Response is expired, record will be removed and Cache won’t return expired response.
Using Mobile Connect Authentication/(Deprecated) 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).
To make a successful authorisation call, you must provide the following additional parameters:
client_name
– specifies the name of the application/service requesting authorisation.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 String startAuth(
@RequestParam(required = false) final DiscoveryResponse discoveryResponse,
@RequestParam(required = false) final String subscriberId, final HttpServletRequest request, final String msisdn,
final String mcc, final String mnc, final String sourceIp)
{
…
final MobileConnectRequestOptions options = new MobileConnectRequestOptions.Builder()
.withAuthenticationOptions(new AuthenticationOptions.Builder()
.withScope(scope)
.withContext((apiVersion.equals(Constants.VERSION_2_0) || apiVersion.equals(Constants.VERSION_2_3) || apiVersion.equals(Constants.VERSION_3_0)) ? Constants.CONTEXT_BINDING_MSG : null)
.withBindingMessage((apiVersion.equals(Constants.VERSION_2_0) || apiVersion.equals(Constants.VERSION_2_3) || apiVersion.equals(Constants.VERSION_3_0)) ? Constants.BINDING_MSG : null)
.withClientName(clientName)
.withLoginHintToken(subscriberIdToken)
.build())
.build();
…
}
The startAuth
method generates URL for authorisation, which is sent to client side application.
public String startAuth(
@RequestParam(required = false) final DiscoveryResponse discoveryResponse,
@RequestParam(required = false) String subscriberId, final HttpServletRequest request, final String msisdn,
final String mcc, final String mnc, final String sourceIp)
{
String scope = operatorParams.getScope();
String subscriberIdToken = discoveryResponse.getResponseData().getSubscriberIdToken();
try {
if (!VersionDetection.getCurrentVersion(apiVersion, scope, discoveryResponse.getProviderMetadata()).equals(DefaultOptions.MC_V3_0)) {
loginHintTokenPreference = false;
}
} catch (InvalidScopeException e) {
e.printStackTrace();
}
if (!loginHintTokenPreference && !StringUtils.isNullOrEmpty(subscriberId)) {
subscriberIdToken = null;
} else if (!StringUtils.isNullOrEmpty(subscriberIdToken)) {
subscriberId = null;
}
final MobileConnectRequestOptions options = new MobileConnectRequestOptions.Builder()
.withAuthenticationOptions(new AuthenticationOptions.Builder()
.withScope(scope)
.withContext((apiVersion.equals(Constants.VERSION_2_0) || apiVersion.equals(Constants.VERSION_2_3) || apiVersion.equals(Constants.VERSION_3_0)) ? Constants.CONTEXT_BINDING_MSG : null)
.withBindingMessage((apiVersion.equals(Constants.VERSION_2_0) || apiVersion.equals(Constants.VERSION_2_3) || apiVersion.equals(Constants.VERSION_3_0)) ? Constants.BINDING_MSG : null)
.withClientName(clientName)
.withLoginHintToken(subscriberIdToken)
.build())
.build();
final MobileConnectStatus status =
this.mobileConnectWebInterface.startAuthentication(request, discoveryResponse, subscriberId,
null, null, options, apiVersion);
if (status.getErrorMessage() != null) {
return null;
}
setSessionCache(status, msisdn, mcc, mnc, sourceIp);
return status.getUrl();
}
Also, please, see
Configuring and running the server side SDK in Without Discovery Mode
- Register an account or login to the Mobile Connect Developer Portal and create an application to obtain your sandbox credentials.
-
Download the Mobile Connect server side project.
git clone https://github.com/Mobile-Connect/java_server_side_library.git
- Open the configuration file:
[local path]\mobile-connect-demo\src\main\resources\config\WithoutDiscoveryData.json
to pass required parameter values for without discovery mode.
Here are 17 parameters:{ "clientID": your client Id, "clientSecret": your client Secret, "clientName": your client Name, "discoveryURL": your Discovery endpoint, "redirectURL": "<protocol>://<hostname>/server_side_api/discovery_callback", "xRedirect": "True", "includeRequestIP": "True", "apiVersion": api version: "mc_v1.1", "mc_v2.0" or "mc_di_r2_v2.3", "scope": scope, "acrValues": acr_values, "MaxDiscoveryCacheSize": max cache size, "operatorUrls": { "authorizationUrl": authorize endpoint, "requestTokenUrl": token endpoint, "userInfoUrl": userinfo endpoint, "premiumInfoUri": premiuminfo endpoint, "providerMetadataUri": provider metadata endpoint } }
-
Open sector_identifier_uri.json file and specify the value of sector_identifier_uri with a single JSON array of redirect_uri values.
["<protocol>://<hostname>/server_side_api/discovery_callback"]
- 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).
- Download and install any missing dependencies.
- Build Demo App using Maven repository:
cd java_server_side_library mvn clean package
- Deploy mobile-connect.war.
- Prepare client side application (IOS or Android application) or Demo App for Server Side application.
Note: if you operate in the EU then you should use EU Discovery Service domain in discovery url: https://eu.discover.mobileconnect.io
.
Using Mobile Connect without Discovery
The server side SDK allows you to call methods that get additional parameters from a JSON configuration file. Once the end user has entered MSISDN and intitiated session from the client side, the server side SDK builds the call to the Authorize endpoint. A successful authentication returns an id_token containing the user’s PCR, which is unpacked and available for verification against your user register.
This SDK allows calls to Mobile Connect without first doing a Discovery call. It is a similar process to usual Mobile Connect Authentication but instead of calls to the Discovery Service a new function makeDiscoveryForAuthorization
is used to create the discoveryOptions
object.
Using Mobile Connect Authentication/(Deprecated) Authorisation
Client side application sends a GET
request with MSISDN to server side application.
Example request:
http://localhost:8080/server_side_api/start_discovery_manually?msisdn=your_msisdn
Then server side application makes startAuthenticationWithoutDiscovery
method call for getting authentication URL.
public RedirectView startAuthenticationWithoutDiscovery(
@RequestParam(required = false) final String msisdn,
final HttpServletRequest request)
{
LOGGER.info("* Attempting discovery for msisdn={}", LogUtils.mask(msisdn, LOGGER, Level.INFO));
DiscoveryResponse discoveryResponse = null;
try {
discoveryResponse = this.mobileConnectWebInterface.generateDiscoveryManually(clientSecret, clientId,
clientName, operatorUrls);
} catch (JsonDeserializationException e) {
LOGGER.warn("Can't create discoveryResponse");
}
String url;
if (operatorParams.getScope().contains(Scope.AUTHZ)) {
url = startAuthorize(
discoveryResponse,
msisdn,
request);
} else {
url = startAuthentication(
discoveryResponse,
msisdn,
request);
}
return new RedirectView(url);
}
Using Mobile Connect Identity and Attributes
Note: Identity Phone Number and Identity National Id are deprecated but still supported in the SDK
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:
- (Deprecated) Identity: Phone Number –
MC_PHONE
- Identity: Sign-up –
MC_SIGNUP
- (Deprecated) Identity: National Identity –
MC_NATIONALID
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_signup” (“mc_identity_signup”) and “mc_nationalid” (“mc_identity_nationalid”) as applicable.
Add the MobileConnectRequestOptions
required for Authorisation and Identity: Phone Number.
Add the requestIdentity()
and requestUserInfo()
method calls to request the identity data following a successful authorisation.
public ModelAndView StateDiscoveryCallback(@RequestParam(required = false) String state,
@RequestParam(required = false) final String error,
@RequestParam(required = false) final String error_description,
@RequestParam(required = false) final String description,
final HttpServletRequest request)
{
...
if (apiVersion.equals(DefaultOptions.VERSION_MOBILECONNECT) && !StringUtils.isNullOrEmpty(sessionData.getDiscoveryResponse().getOperatorUrls().getUserInfoUrl())) {
for (String userInfoScope : userinfoScopes) {
if (operatorParams.getScope().contains(userInfoScope)) {
final MobileConnectStatus statusUserInfo =
this.mobileConnectWebInterface.requestUserInfo(request, sessionData.getDiscoveryResponse(),
status.getRequestTokenResponse().getResponseData().getAccessToken());
status = status.withIdentityResponse(statusUserInfo.getIdentityResponse());
break;
}
}
} else if ((apiVersion.equals(DefaultOptions.MC_V3_0) || apiVersion.equals(DefaultOptions.MC_V2_3) || apiVersion.equals(DefaultOptions.MC_V2_0))
&& !StringUtils.isNullOrEmpty(sessionData.getDiscoveryResponse().getOperatorUrls().getPremiumInfoUri())) {
for (String identityScope : identityScopes) {
if (operatorParams.getScope().contains(identityScope)) {
final MobileConnectStatus statusIdentity =
this.mobileConnectWebInterface.requestIdentity(request, sessionData.getDiscoveryResponse(),
status.getRequestTokenResponse().getResponseData().getAccessToken());
status = status.withIdentityResponse(statusIdentity.getIdentityResponse());
break;
}
}
} else {
return redirectToView(status, Status.TOKEN);
}
...
}