In order to be able to access ONE Reporting service, a client should add
Authorization: Bearer TOKEN
header to each request.
The client should be provisioned with both client_id and client_secret for QA/DEV and PROD environments.
After these values are obtained, client_id should be registered within ONE Reporting system.
Note: token url for stage (UAT) https://id-uat.b2b.yahooinc.com/identity/oauth2/access_token
Note: token url for PROD https://id.b2b.yahooinc.com/identity/oauth2/access_token
Note: client_secret is always private and should never be shared.
Below is the code sample for OAuth 2.0 access token generation for ONE Reporting.
package oauth2;
import java.util
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import org.jose4j.jws.{AlgorithmIdentifiers, JsonWebSignature}
import org.jose4j.jwt.{JwtClaims, NumericDate}
import org.jose4j.keys.HmacKey
import org.springframework.http.converter.FormHttpMessageConverter
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
import org.springframework.http.{HttpEntity, HttpHeaders, HttpMethod, MediaType}
import org.springframework.util.{LinkedMultiValueMap, MultiValueMap}
import org.springframework.web.client.RestTemplate
import scala.collection.JavaConverters._
/**
* Is used to retrieve OAuth 2.0 access token in Identity B2B platform
*/
object AccessTokenFetchService {
/** Fetch the token by the provided credentials
* @param credentials see: [[Credentials]]
* @return OAuth 2.0 access token along, see: [[AccessToken]]
*/
def fetch(credentials: Credentials): AccessToken = {
val payload = constructPayload(credentials)
val httpEntity = new HttpEntity[MultiValueMap[String, String]](payload, prepareHttpHeaders)
// token request is a POST HTTP request on the Identity B2B platform endpoint url
// with the payload consisting of url-encoded query params
restTemplate.exchange(credentials.tokenUrl, HttpMethod.POST, httpEntity, classOf[AccessToken]).getBody
}
// payload key-value pairs
private def constructPayload(credentials: Credentials): MultiValueMap[String, String] = {
val map = Map(
"client_assertion_type" -> credentials.clientAssertionType,
"grant_type" -> credentials.grantType,
"scope" -> credentials.scope,
"realm" -> credentials.realm,
"client_assertion" -> generateJsonWebToken(credentials)
).map { case (name, value) => name -> Seq(value).asJava }.asJava
new LinkedMultiValueMap[String, String](map)
}
// JWT token generation by the provided credentials
private def generateJsonWebToken(credentials: Credentials): String = {
val claims = new JwtClaims
claims.setIssuedAt(NumericDate.now)
claims.setExpirationTimeMinutesInTheFuture(5)
claims.setSubject(credentials.clientId)
claims.setIssuer(credentials.clientId)
claims.setAudience(getAudience(credentials.tokenUrl, credentials.realm))
claims.setGeneratedJwtId()
val key = new HmacKey(credentials.clientSecret.getBytes("UTF-8"))
val jws = new JsonWebSignature
jws.setPayload(claims.toJson)
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.HMAC_SHA256)
jws.setKey(key)
jws.setDoKeyValidation(false)
jws.getCompactSerialization
}
private def getAudience(url: String, realm: String) = String.format("%s?realm=%s", url, realm)
// HTTP headers expected by Identity B2B platform
private def prepareHttpHeaders = {
val headers = new HttpHeaders
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED)
headers.setAccept(Seq(MediaType.APPLICATION_JSON).asJava)
headers
}
// spring-web rest template to make http calls
private lazy val restTemplate = {
val restTemplate = new RestTemplate
val mapper = new ObjectMapper
mapper.registerModule(DefaultScalaModule)
val jacksonConverter = new MappingJackson2HttpMessageConverter(mapper)
restTemplate.setMessageConverters(util.Arrays.asList(jacksonConverter, new FormHttpMessageConverter))
restTemplate
}
}
package oauth2;
/**
* The credentials needed for OAuth 2.0 token
* generation in Identity B2B platform
*/
trait Credentials {
def tokenUrl: String
def scope: String
def clientId: String
def clientSecret: String
def realm: String
def grantType: String
def clientAssertionType: String
}
package oauth2;
/**
* Container for the credentials to generate One Reporting OAuth 2.0 token
* @param tokenUrl identity B2B platform token generation endpoint url
* @param clientId the provided by One Central client id
* @param clientSecret the provided by One Central client secret
* @param scope OAuth 2.0 scope, defaults to "one"
* @param realm OAuth 2.0 realm, defaults to "aolcorporate/aolexternals"
* @param grantType OAuth 2.0 grant type, defaults to "client_credentials"
* @param clientAssertionType OAuth 2.0 assertion type with the default value
*/
case class ReportingCredentials(tokenUrl: String,
clientId: String,
clientSecret: String,
scope: String = "one",
realm: String = "aolcorporate/aolexternals",
grantType: String = "client_credentials",
clientAssertionType: String = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer")
extends Credentials
package oauth2;
import com.fasterxml.jackson.annotation.JsonProperty
/**
* Identity B2B platform successful response on token generation
* @param token the generated access token
* @param scope OAuth 2.0 scope of the generated access token
* @param tokenType OAuth 2.0 token type (Bearer)
* @param expiresIn expiration time in seconds
*/
case class AccessToken(@JsonProperty("access_token") token: String,
@JsonProperty("scope") scope: String,
@JsonProperty("token_type") tokenType: String,
@JsonProperty("expires_in") expiresIn: Int)