001/* 002 * Copyright 2024 Vonage 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.vonage.client.camara.numberverification; 017 018import com.vonage.client.DynamicEndpoint; 019import com.vonage.client.HttpWrapper; 020import com.vonage.client.RestEndpoint; 021import com.vonage.client.VonageClient; 022import com.vonage.client.auth.camara.FrontendAuthRequest; 023import com.vonage.client.auth.camara.NetworkAuthMethod; 024import com.vonage.client.auth.camara.TokenRequest; 025import com.vonage.client.camara.CamaraResponseException; 026import com.vonage.client.camara.NetworkApiClient; 027import com.vonage.client.common.HttpMethod; 028import java.net.URI; 029import java.util.Objects; 030import java.util.UUID; 031 032/** 033 * A client for communicating with the Vonage Number Verification API. The standard way to obtain an instance 034 * of this class is to use {@link VonageClient#getNumberVerificationClient()}. 035 */ 036public class NumberVerificationClient extends NetworkApiClient { 037 final RestEndpoint<VerifyNumberRequest, VerifyNumberResponse> verifyNumber; 038 private final UUID appId; 039 private VerifyNumberRequest cachedRequest; 040 041 /** 042 * Create a new NumberVerificationClient. 043 * 044 * @param wrapper Http Wrapper used to create requests. 045 */ 046 @SuppressWarnings("unchecked") 047 public NumberVerificationClient(HttpWrapper wrapper) { 048 super(wrapper); 049 appId = wrapper.getApplicationId(); 050 051 verifyNumber = DynamicEndpoint.<VerifyNumberRequest, VerifyNumberResponse> builder(VerifyNumberResponse.class) 052 .authMethod(NetworkAuthMethod.class).requestMethod(HttpMethod.POST) 053 .responseExceptionType(CamaraResponseException.class).pathGetter((de, req) -> { 054 setNetworkAuth(new TokenRequest(req.redirectUrl, req.getCode())); 055 return getCamaraBaseUri() + "number-verification/v031/verify"; 056 }) 057 .wrapper(wrapper).build(); 058 } 059 060 /** 061 * Sets up the client for verifying the given phone number. This method will cache the provided 062 * number and redirect URL for verification when calling {@link #verifyNumber(String)}. 063 * The intended usage is to call this method first, and follow the returned URL on the target 064 * device which the phone number is supposed to be associated with. When the URL is followed, 065 * it will trigger an inbound request to the {@code redirectUrl} with two query parameters: 066 * {@code CODE} and {@code STATE} (if provided as a parameter to this method). The code should 067 * then be extracted from the query parameters and passed to {@link #verifyNumber(String)}. 068 * 069 * @param phoneNumber The MSISDN to verify. 070 * @param redirectUrl Redirect URL, as set in your Vonage application for Network APIs. 071 * @param state An optional string for identifying the request. For simplicity, this could 072 * be set to the same value as {@code phoneNumber}, or it may be {@code null}. 073 * 074 * @return A link with appropriate parameters which should be followed on the end user's device. 075 * The link should be followed when using the SIM card associated the provided phone number. 076 * Therefore, on the target device, Wi-Fi should be disabled when doing this, otherwise the result 077 * of {@link #verifyNumber(String)} will be {@code false}. 078 */ 079 public URI initiateVerification(String phoneNumber, URI redirectUrl, String state) { 080 cachedRequest = new VerifyNumberRequest(phoneNumber, redirectUrl); 081 return new FrontendAuthRequest(phoneNumber, redirectUrl, appId, state).buildOidcUrl(); 082 } 083 084 /** 085 * Exchanges the code for an access token, makes the API call to the 086 * Number Verification API's "verify" endpoint and extracts the result. 087 * 088 * @param code The code obtained from the inbound callback's query parameters. 089 * 090 * @return {@code true} if the device that followed the link was using the SIM card associated 091 * with the phone number provided in {@linkplain #initiateVerification(String, URI, String)}, 092 * {@code false} otherwise (e.g. it was unknown, the link was not followed, the device that followed 093 * the link didn't use the SIM card with that phone number when doing so). 094 * 095 * @throws com.vonage.client.auth.camara.NetworkAuthResponseException If there was an error 096 * exchanging the code for an access token when using the Vonage Network Auth API. 097 * 098 * @throws CamaraResponseException If there was an error in communicating with the Number Verification API. 099 */ 100 public boolean verifyNumber(String code) { 101 if (cachedRequest == null) { 102 throw new IllegalStateException("You must first call initiateVerification using this client."); 103 } 104 return verifyNumber(cachedRequest.withCode(code)); 105 } 106 107 /** 108 * Stateless implementation of {@link #verifyNumber(String)}, which creates a new request 109 * without having to call {@link #initiateVerification(String, URI, String)} first. This is 110 * useful in cases where concurrent verifications are required, or the URL is obtained another way. 111 * 112 * @param phoneNumber MSISDN of the target device to verify. 113 * @param redirectUri Redirect URL, as set in your Vonage application for Network APIs. 114 * @param code The code obtained from the inbound callback's query parameters. 115 * 116 * @return {@code true} if the device that followed the link was using the SIM card associated 117 * with the phone number provided in {@linkplain #initiateVerification(String, URI, String)}, 118 * {@code false} otherwise (e.g. it was unknown, the link was not followed, the device that followed 119 * the link didn't use the SIM card with that phone number when doing so). 120 * 121 * @throws com.vonage.client.auth.camara.NetworkAuthResponseException If there was an error 122 * exchanging the code for an access token when using the Vonage Network Auth API. 123 * 124 * @throws CamaraResponseException If there was an error in communicating with the Number Verification API. 125 */ 126 public boolean verifyNumber(String phoneNumber, URI redirectUri, String code) { 127 return verifyNumber(new VerifyNumberRequest(phoneNumber, redirectUri).withCode(code)); 128 } 129 130 private boolean verifyNumber(VerifyNumberRequest request) { 131 try { 132 return verifyNumber.execute(request).getDevicePhoneNumberVerified(); 133 } 134 finally { 135 cachedRequest = null; 136 } 137 } 138}