Synchroniser les droits d'accès des utilisateurs (intégration côté serveur)

Les éditeurs utilisent principalement l'intégration côté serveur pour gérer les lecteurs et leurs droits d'accès. Ils se servent de UpdateReaderEntitlements pour mettre à jour l'enregistrement Google du droit d'accès d'un ID produit pour un PPID.

Configurer l'API dans Google Cloud

La configuration de l'API Subscription Linking dans Google Cloud se fait en deux étapes :

  1. Activer l'API pour un projet donné
  2. Créer un compte de service pour accéder à l'API

Activer l'API Subscription Linking

Pour utiliser un compte de service et gérer les droits d'accès d'un lecteur, l'API Subscription Linking doit être activée et un compte de service OAuth doit être correctement configuré pour un projet Google Cloud. Pour activer l'API Subscription Linking pour un projet, accédez au menu -> API et services -> Bibliothèque et recherchez Subscription Linking, ou accédez directement à la page suivante :


https://console.cloud.google.com/apis/library?project=gcp_project_id

api

Figure 1. Accéder à la bibliothèque d'API et activer l'API pour un projet Google Cloud.

Créer un compte de service

Les comptes de service permettent d'autoriser l'accès à l'API Subscription Linking depuis votre application.

  1. Créez un compte de service dans la console de votre projet.
  2. Créez des identifiants pour le compte de service et enregistrez le fichier credentials.json dans un emplacement sécurisé accessible par votre application.
  3. Accordez le rôle IAM "Administrateur d'associations d'abonnements" au compte de service que vous avez créé. Pour contrôler précisément les fonctionnalités du compte de service, vous pouvez attribuer le rôle approprié en vous référant au tableau ci-dessous.
Fonctionnalité/Rôle Administrateur d'associations d'abonnements Lecteur d'associations d'abonnements Lecteur des droits d'accès aux associations d'abonnements
Obtenir les droits d'accès des lecteurs
Obtenir des lecteurs
Mettre à jour les droits d'accès des lecteurs
Supprimer des lecteurs

Utiliser des comptes de service avec l'API Subscription Linking

Pour authentifier les appels à l'API Subscription Linking avec des comptes de service, utilisez la bibliothèque cliente googleapis (qui gère automatiquement les requêtes access_token) ou signez les requêtes directement avec l'API REST. Si vous utilisez l'API REST, vous devez d'abord obtenir un access_token (via la bibliothèque Google Auth ou à l'aide d'un jeton JWT de compte de service), puis l'inclure dans l'en-tête Authorization.

Les exemples de bibliothèque cliente et d'API REST suivants contiennent des exemples de code pour appeler getReader(), getReaderEntitlements(), updateReaderEntitlements() et deleteReader().

Bibliothèque cliente

Cette section explique comment utiliser la bibliothèque cliente googleapis dans Node.js.

Exemple de demande

Pour le champ keyFile dans le constructeur Auth.GoogleAuth, définissez le chemin d'accès à votre clé de compte de service. Si vous ne pouvez pas exporter de clé de compte de service en raison de votre règle d'administration, vous pouvez utiliser la méthode Identifiants par défaut du compte (ADC). Si vous choisissez la méthode ADC, vous n'avez pas besoin de fournir le champ keyFile, car ADC recherchera les identifiants par lui-même.

import {readerrevenuesubscriptionlinking_v1, Auth} from 'googleapis';
const subscriptionLinking = readerrevenuesubscriptionlinking_v1.Readerrevenuesubscriptionlinking;

class SubscriptionLinking {
  constructor() {
    this.auth = new Auth.GoogleAuth({
      keyFile: process.env.KEY_FILE,
      scopes: [
        'https://www.googleapis.com/auth/readerrevenue.subscriptionlinking.manage'
      ],
    })
  }

  init() {
    return new subscriptionLinking(
        {version: 'v1', auth: this.auth})
  }
}

const subscriptionLinkingApi = new SubscriptionLinking();
const client = subscriptionLinkingApi.init();

/**
 * Retrieves details for a specific reader associated with the publication.
 * @async
 * @param {string} ppid - The Publisher Provided ID (ppid) for the reader.
 * @return {Promise<object>} A promise that resolves with the reader's details
 *  from the API.
 */
async function getReader(ppid) {
  const publicationId = process.env.PUBLICATION_ID;
  return await client.publications.readers.get({
    name: `publications/${publicationId}/readers/${ppid}`,
  });
}

/**
 * Updates the entitlements for a specific reader.
 * @async
 * @param {string} ppid - The Publisher Provided ID (ppid) for the reader whose
 *  entitlements are being updated.
 * @return {Promise<object>} A promise that resolves with the result of the
 *  updated entitlements object.
 */
async function updateReaderEntitlements(ppid) {
  const publicationId = process.env.PUBLICATION_ID;
  const requestBody = {
    /*
    Refer to
    https://developers.google.com/news/subscribe/subscription-linking/appendix/glossary#entitlements_object
    */
    entitlements : [{
      product_id: `${publicationId}:basic`,
      subscription_token: 'abc1234',
      detail: 'This is our basic plan',
      expire_time: '2025-10-21T03:05:08.200564Z'
    }]
  };
  return await client.publications.readers.updateEntitlements({
    name: `publications/${publicationId}/readers/${ppid}/entitlements`,
    requestBody
  });
}

/**
 * Retrieves the current entitlements for a specific reader.
 * @async
 * @param {string} ppid - The Publisher Provided ID (ppid) for the reader.
 * @return {Promise<object>} A promise that resolves with the reader's entitlements object.
 */
async function getReaderEntitlements(ppid) {
  const publicationId = process.env.PUBLICATION_ID;
  return await client.publications.readers.getEntitlements({
    name: `publications/${publicationId}/readers/${ppid}/entitlements`
  });
}

/**
 * Deletes a specific Subscription Linkikng reader record associated with the publication.
 * @async
 * @param {string} ppid - The Publisher Provided ID (ppid) for the reader to be deleted.
 * @param {boolean=} forceDelete - If true, delete the user even if their
 *  entitelements are not empty
 * @return {Promise<object>} A promise that resolves upon successful deletion
 *  with an empty object ({})
 */
async function deleteReader(ppid, forceDelete = false) {
  const publicationId = process.env.PUBLICATION_ID;
  return await client.publications.readers.delete({
    name: `publications/${publicationId}/readers/${ppid}`
    force: forceDelete
  });
}

API REST

Si vous souhaitez appeler des points de terminaison d'API REST, vous pouvez utiliser l'une des méthodes pour obtenir accessToken et le définir sur l'en-tête Authorization.

1. Utiliser la bibliothèque GoogleAuth

Pour la clé credentials, vous pouvez utiliser une clé de compte de service ou des identifiants par défaut de compte (ADC). Si vous choisissez la méthode ADC, vous n'avez pas besoin de fournir le champ credentials, car ADC recherchera les identifiants par lui-même.

import { GoogleAuth } from 'google-auth-library';
import credentialJson from 'path_to_your_json_file' with { type: 'json' };

const auth = new GoogleAuth({
    credentials: credential_json,
    scopes: [
      'https://www.googleapis.com/auth/readerrevenue.subscriptionlinking.manage'
    ]
});

async function getAccessToken() {
    const accessToken = await auth.getAccessToken();
    return accessToken;
}

2. Générer un access_token à l'aide d'un jeton JWT de compte de service

import fetch from 'node-fetch';
import jwt from 'jsonwebtoken';

function getSignedJwt() {
  /*
    Either store the service account credentials string in an environmental variable
    Or implement logic to fetch it.
  */
  const key_file = process.env.CREDENTIALS_STRING

  const issueDate = new Date();
  const expireMinutes = 60;
  const offsetInSeconds = issueDate.getTimezoneOffset() * 60000;
  const expireDate = new Date(issueDate.getTime() + (expireMinutes * 60000));
  const iat = Math.floor((issueDate.getTime() + offsetInSeconds) / 1000);
  const exp = Math.floor((expireDate.getTime() + offsetInSeconds) / 1000);

  const token = {
    iss: key_file.client_email,
    iat,
    exp,
    aud: 'https://oauth2.googleapis.com/token',
    scope:'https://www.googleapis.com/auth/readerrevenue.subscriptionlinking.manage',
  }
  return jwt.sign(token, key_file.private_key, {
    algorithm: 'RS256',
    keyid: key_file.private_key_id,
  })
}

async function getAccessToken(signedJwt) {
  let body = new URLSearchParams();
  body.set('grant_type', 'urn:ietf:params:oauth:grant-type:jwt-bearer');
  body.set('assertion', signedJwt);
  const response = await fetch('https://oauth2.googleapis.com/token', {
    method: 'POST',
    headers: {'Content-Type': 'application/x-www-form-urlencoded'},
    body
  })
  const accessResponse = await response.json();
  return accessResponse.access_token;
}

Exemple de code pour les appels d'API REST avec la bibliothèque Google Auth

import { GoogleAuth } from 'google-auth-library';
import fetch from 'node-fetch'
import credentialJson from 'path_to_your_json_file' with { type: 'json' };

const BASE_SUBSCRIPTION_LINKING_API_URL='https://readerrevenuesubscriptionlinking.googleapis.com/v1';
const publicationId = process.env.PUBLICATION_ID

const auth = new GoogleAuth({
    credentials: credentialJson,
    scopes: [
      'https://www.googleapis.com/auth/readerrevenue.subscriptionlinking.manage'
    ]
});

async function getAccessToken() {
    const accessToken = await auth.getAccessToken();
    return accessToken;
}

/**
 * Retrieves details for a specific reader associated with the publication.
 * @async
 * @param {string} ppid - The Publisher Provided ID (ppid) for the reader.
 * @return {object} reader json for the given ppid
 */
async function getReader(ppid) {
  const endpoint = `${BASE_SUBSCRIPTION_LINKING_API_URL}/publications/${publicationId}/readers/${ppid}`;
  const accessToken = await getAccessToken();
  const response = await fetch(endpoint, {
     method: 'GET',
     headers: {
       Authorization: `Bearer ${accessToken}`,
     },
   });
  const reader = await response.json();
  return reader;
}

/**
 * Updates the entitlements for a specific reader.
 * @async
 * @param {string} ppid - The Publisher Provided ID (ppid) for the reader.
 * @return {object} the updated entitlements object in json.
 */
async function updateReaderEntitlements(ppid) {
  const endpoint = `${BASE_SUBSCRIPTION_LINKING_API_URL}/publications/${publicationId}/readers/${ppid}/entitlements`;
  const requestBody = {
    /*
    Refer to
    https://developers.google.com/news/subscribe/subscription-linking/appendix/glossary#entitlements_object
    */
    entitlements : [{
      product_id: `${publicationId}:basic`,
      subscription_token: 'abc1234',
      detail: 'This is our basic plan',
      expire_time: '2025-10-21T03:05:08.200564Z'
    }]
  };
  const response = await fetch(endpoint, {
     method: 'PATCH',
     headers: {
       Authorization: `Bearer ${accessToken}`,
       'Content-Type': 'application/json',
     },
     body: JSON.stringify(requestBody)
   })
  const updatedEntitlements = await response.json();
  return updatedEntitlements;
}

/**
 * Retrieves the current entitlements for a specific reader.
 * @async
 * @param {string} ppid - The Publisher Provided ID (ppid) for the reader.
 * @return {object} the reader's entitlements object in json.
 */
async function getReaderEntitlements(ppid) {
  const endpoint = `${BASE_SUBSCRIPTION_LINKING_API_URL}/publications/${publicationId}/readers/${ppid}/entitlements`;
  const accessToken = await getAccessToken();
  const response = await fetch(endpoint, {
     method: 'GET',
     headers: {
       Authorization: `Bearer ${accessToken}`,
     },
   });
  const entitlements = await response.json();
  return entitlements;
}

/**
 * Deletes a specific Subscription Linkikng reader record associated with the publication.
 * @async
 * @param {string} ppid - The Publisher Provided ID (ppid) for the reader.
 * @param {boolean=} forceDelete - If true, delete the user even if their
 *  entitelements are not empty
 * @return {object} returns an empty object ({}) if the delete operation is successful
 */
async function deleteReader(ppid, forceDelete = false) {
  const endpoint = `${BASE_SUBSCRIPTION_LINKING_API_URL}/publications/${publicationId}/readers/${ppid}?force=${forceDelete}`;
  const response = await fetch(endpoint, {
     method: 'DELETE',
     headers: {
       Authorization: `Bearer ${accessToken}`,
     }
   });
  const result = await response.json();
  return result;
}