11import type { Settings } from './generated-types'
2- import { createHmac } from 'crypto '
3- import { CredsObj , YahooSubTaxonomy } from './types'
2+ import type { ModifiedResponse } from '@segment/actions-core '
3+ import { CredsObj , YahooSubTaxonomy , TokenResponse } from './types'
44import { RequestClient , IntegrationError } from '@segment/actions-core'
55import { StatsClient } from '@segment/actions-core/destination-kit'
6+ import { generate_jwt } from './utils-rt'
7+
8+ // Constants for Yahoo Taxonomy API
9+ const TAXONOMY_CLIENT_KEY_PREFIX = 'idb2b.dsp.datax'
10+ const TAXONOMY_TOKEN_ENDPOINT = 'https://id.b2b.yahooincapis.com/zts/v1/oauth2/token'
11+ const TAXONOMY_AUDIENCE_URL = 'https://id.b2b.yahooincapis.com/zts/v1'
12+ const TAXONOMY_SCOPE = 'idb2b.dsp.datax:role.online.writer'
613
714export function gen_customer_taxonomy_payload ( settings : Settings ) {
815 const data = {
@@ -45,26 +52,45 @@ export function gen_random_id(length: number): string {
4552 return random_id . join ( '' )
4653}
4754
48- export function gen_oauth1_signature ( client_key : string , client_secret : string , method : string , url : string ) {
49- // Following logic in #9 https://oauth.net/core/1.0a/#sig_norm_param
50- const timestamp = Math . floor ( new Date ( ) . getTime ( ) / 1000 )
51- const nonce = gen_random_id ( 15 )
55+ /**
56+ * Obtains a short-lived OAuth 2.0 Bearer token for the Taxonomy API using the
57+ * same JWT client-credentials flow used by the Online (Realtime) API.
58+ * @param request RequestClient for making HTTP requests
59+ * @param tx_client_key Taxonomy API client key (will be prefixed with 'idb2b.dsp.datax.')
60+ * @param tx_client_secret Taxonomy API client secret
61+ * @returns OAuth 2.0 access token
62+ */
63+ export async function get_taxonomy_access_token (
64+ request : RequestClient ,
65+ tx_client_key : string ,
66+ tx_client_secret : string
67+ ) : Promise < string > {
68+ // Prefix the client key as required by Yahoo's Taxonomy API
69+ const prefixed_client_key = `${ TAXONOMY_CLIENT_KEY_PREFIX } .${ tx_client_key } `
70+
71+ // Generate JWT using the shared utility
72+ const jwt = generate_jwt ( prefixed_client_key , tx_client_secret )
73+
74+ const res : ModifiedResponse < TokenResponse > = await request < TokenResponse > ( TAXONOMY_TOKEN_ENDPOINT , {
75+ method : 'POST' ,
76+ body : new URLSearchParams ( {
77+ client_assertion : jwt ,
78+ client_assertion_type : 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer' ,
79+ grant_type : 'client_credentials' ,
80+ scope : TAXONOMY_SCOPE ,
81+ aud : TAXONOMY_AUDIENCE_URL
82+ } )
83+ } )
5284
53- const param_string = `oauth_consumer_key=${ encodeURIComponent ( client_key ) } &oauth_nonce=${ encodeURIComponent (
54- nonce
55- ) } &oauth_signature_method=${ encodeURIComponent ( 'HMAC-SHA1' ) } &oauth_timestamp=${ encodeURIComponent (
56- timestamp
57- ) } &oauth_version=${ encodeURIComponent ( '1.0' ) } `
85+ if ( ! res ?. data ?. access_token ) {
86+ throw new IntegrationError (
87+ 'Failed to obtain taxonomy access token - missing access_token in response' ,
88+ 'YAHOO_TAXONOMY_TOKEN_ERROR' ,
89+ 500
90+ )
91+ }
5892
59- const base_string = `${ method . toUpperCase ( ) } &${ encodeURIComponent ( url ) } &${ encodeURIComponent ( param_string ) } `
60- const encoded_client_secret = encodeURIComponent ( client_secret )
61- const signature = encodeURIComponent (
62- createHmac ( 'sha1' , encoded_client_secret + '&' )
63- . update ( base_string )
64- . digest ( 'base64' )
65- )
66- const oauth1_auth_string = `OAuth oauth_consumer_key="${ client_key } ", oauth_nonce="${ nonce } ", oauth_signature="${ signature } ", oauth_signature_method="HMAC-SHA1", oauth_timestamp="${ timestamp } ", oauth_version="1.0"`
67- return oauth1_auth_string
93+ return res . data . access_token
6894}
6995
7096export async function update_taxonomy (
@@ -78,13 +104,16 @@ export async function update_taxonomy(
78104 const tx_client_secret = tx_creds . tx_client_secret
79105 const tx_client_key = tx_creds . tx_client_key
80106 const url = `https://datax.yahooapis.com/v1/taxonomy/append${ engage_space_id . length > 0 ? '/' + engage_space_id : '' } `
81- const oauth1_auth_string = gen_oauth1_signature ( tx_client_key , tx_client_secret , 'PUT' , url )
107+
108+ // Get a short-lived Bearer token using the same JWT client-credentials flow as the Online API
109+ const access_token = await get_taxonomy_access_token ( request , tx_client_key , tx_client_secret )
110+
82111 try {
83112 const add_segment_node = await request ( url , {
84113 method : 'PUT' ,
85114 body : body_form_data ,
86115 headers : {
87- Authorization : oauth1_auth_string ,
116+ Authorization : `Bearer ${ access_token } ` ,
88117 'Content-Type' : 'multipart/form-data; boundary=SEGMENT-DATA'
89118 }
90119 } )
0 commit comments