{"id":21625305,"url":"https://github.com/charlespalmerbf/google-jwt-to-bearer","last_synced_at":"2026-05-14T05:31:27.727Z","repository":{"id":211445800,"uuid":"657113939","full_name":"charlespalmerbf/google-jwt-to-bearer","owner":"charlespalmerbf","description":"A node script for converting a JKF (JSON Key File) into a JWT (JSON Web Token) and then into a Google OAuth 2.0 Bearer Token.","archived":false,"fork":false,"pushed_at":"2024-01-19T09:56:18.000Z","size":123,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-18T19:55:51.777Z","etag":null,"topics":["bearer-tokens","google","google-api","jwt-authentication","nodejs","oauth2"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/charlespalmerbf.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-06-22T10:52:50.000Z","updated_at":"2023-12-14T23:41:33.000Z","dependencies_parsed_at":"2025-03-19T02:31:29.994Z","dependency_job_id":null,"html_url":"https://github.com/charlespalmerbf/google-jwt-to-bearer","commit_stats":null,"previous_names":["charlespalmerbf/google-jwt-to-bearer"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/charlespalmerbf/google-jwt-to-bearer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/charlespalmerbf%2Fgoogle-jwt-to-bearer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/charlespalmerbf%2Fgoogle-jwt-to-bearer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/charlespalmerbf%2Fgoogle-jwt-to-bearer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/charlespalmerbf%2Fgoogle-jwt-to-bearer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/charlespalmerbf","download_url":"https://codeload.github.com/charlespalmerbf/google-jwt-to-bearer/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/charlespalmerbf%2Fgoogle-jwt-to-bearer/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33012266,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-13T13:14:54.681Z","status":"online","status_checked_at":"2026-05-14T02:00:06.663Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bearer-tokens","google","google-api","jwt-authentication","nodejs","oauth2"],"created_at":"2024-11-25T01:08:42.599Z","updated_at":"2026-05-14T05:31:27.709Z","avatar_url":"https://github.com/charlespalmerbf.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"Validating a Google subscription receipt\n=======================\n\nThis is a Node.js script that can be used to validate Google Play Store receipts for Android in-app purchases using server-side validation. It leverages Google's Android Publisher API to verify subscriptions and products.\n\nPrerequisites\n-------------\n\nBefore running this script, ensure you have the following:\n\n-   Node.js installed on your machine.\n-   These values in a .env file:\n    - `TTP_USER_PASSWORD`: Your user password\n    - `API_BASE`: Base URL for the API\n    - `ANDROID_BUNDLE_ID`: Your Android app's bundle ID\n \nUsage\n-----\nThe provided Node.js script can be used to validate a users latest in app subscription using their purchase token. \n\nThe purchase token is saved to the users account at the point of purchase and is returned at the point of login within the `lastpaymenttoken` field.\n\nTo validate an Android subscription, we make a `GET` request to this url:\n\n`https://androidpublisher.googleapis.com/androidpublisher/v3/applications/${process.env.ANDROID_BUNDLE_ID}/purchases/${type}/${fullReceipt.productId}/tokens/${fullReceipt.purchaseToken}?access_token=${accessTokenInfo.access_token}`\n\n-   To this url, we pass five values:\n    - `process.env.ANDROID_BUNDLE_ID`: This is the applications bundle id, and comes from the .env file.\n    - `type`: This indicates if we are verifying a sandbox or production purchase.\n    - `fullReceipt.productId`: This is the product id from the most recent subscription purchase, this comes from the `lastpaymenttoken` retrieved from the user login.\n    - `fullReceipt.purchaseToken`: This is the token for the most recent subscription purchase, this comes from the `lastpaymenttoken` retrieved from the user login.\n    - `accessTokenInfo.access_token`: This value comes from the jwt to bearer function we have already implemented.\n      \nVerifying an Android subscriptions validity\n-----\n\nTo validate an Android subscription, we should first ensure that the subscription we are trying to validate is an Android subscription, this can be done by checking the payment platform specified on the Subscription record in Joomla.\n\nWe then need both the `productId` and `purchaseToken` from the `lastpaymenttoken`, which will need to be parsed first as its stored as a stringified json object, if either of these attributes are unavailable, we should not attempt to validate the subscription. (This may be the case for some legacy accounts which will need to be ported over manually.)\n\n-   When checking if a subscription is valid, we can use three values returned within the data object when making a `GET` request to the url above:\n    - `expiryTimeMillis`: The expiry time of the most recent subscription, in ms (milliseconds).\n    - `autoRenewing`: If the subscription is set to auto renew. This will be a boolean value.\n    - `cancelReason`: This value will represent the reasoning behind the user cancelling their subscription, a data map has been provided within the Node.js script to cover the available reasonings.\n \nIf the active subscription IS NOT past its expiry time, IS marked as auto renewing and DOES NOT have a `cancelReason` attribute, we can consider this subscription valid.\n\nIf the active subscription IS NOT past its expiry, but ISN'T marked as auto renewing and has a `cancelReason`, the subscription should remain active BUT should be marked as cancelled in Joomla, this will then restrict access at the end of the billing period. \n\nThe Node.js script logs an `isValid` calculation to the console factoring in the scenario described above.\n\nValidating an Apple subscription receipt\n=======================\n\nThis Node.js script allows you to validate Apple subscription receipts using the iTunes verifyReceipt endpoint. It checks the validity of a subscription based on the latest receipt information and provides insights into the subscription's status, expiration, and auto-renewal.\n\nPrerequisites\n-------------\n\nBefore running this script, ensure you have the following:\n\n-   Node.js installed on your machine.\n-   These values in a .env file:\n    - `TTP_USER_PASSWORD`: Your user password\n    - `API_BASE`: Base URL for the API\n    - `IOS_SHARED_SECRET`: Your iOS app's shared secret\n \nUsage\n-----\nThe provided Node.js script can be used to validate a users latest in app subscription using their purchase token. \n\nThe full purchase receipt is saved to the users account at the point of purchase and is returned at the point of login within the `lastpaymenttoken` field.\n\nTo validate an Apple subscription, we make a `POST` request to this url:\n\n`https://sandbox.itunes.apple.com/verifyReceipt`\n\nPassing in a request body:\n\n`\n{\n    \"receipt-data\": fullReceipt.transactionReceipt,\n    password: process.env.IOS_SHARED_SECRET,\n}\n`\n\n-   The values included in the request body are as follows:\n    - `transactionReceipt`: This is the token for the most recent subscription purchase, this comes from the `lastpaymenttoken` retrieved from the user login.\n    - `process.env.IOS_SHARED_SECRET`: Your iOS app's shared secret.\n      \nVerifying an Apple subscriptions validity\n-----\n\nTo validate an Apple subscription, we should first ensure that the subscription we are trying to validate is an Apple subscription, this can be done by checking the payment platform specified on the Subscription record in Joomla.\n\nWe then need `transactionReceipt` from the `lastpaymenttoken`, which will need to be parsed first as its stored as a stringified json object, if either of these attributes are unavailable, we should not attempt to validate the subscription. (This may be the case for some legacy accounts which will need to be ported over manually.)\n\n-   When checking if a subscription is valid, we can use three values returned within the data object when making a `POST` request to the url above:\n    - `mostRecentPurchase.expires_date_ms`: The expiry time of the most recent subscription, in ms (milliseconds).\n    - `pending_renewal_info[0].auto_renew_status`: If the subscription is set to auto renew. This will be a boolean value.\n    - `pending_renewal_info[0].expiration_intent`: This value will represent the reasoning behind the user cancelling their subscription, a data map has been provided within the Node.js script to cover the available reasonings.\n \nIf the active subscription IS NOT past its expiry time, IS marked as auto renewing and DOES NOT have a `pending_renewal_info[0].expiration_intent` attribute, we can consider this subscription valid.\n\nIf the active subscription IS NOT past its expiry, but ISN'T marked as auto renewing and has a `pending_renewal_info[0].expiration_intent`, the subscription should remain active BUT should be marked as cancelled in Joomla, this will then restrict access at the end of the billing period. \n\nThe Node.js script logs an `isValid` calculation to the console factoring in the scenario described above.\n\nAndroid \u0026 iOS Subscription Webhook\n-----\n\nA Webhook has been setup for both Android and iOS subscriptions, this Webhook will trigger and send a post request to a specificed endpoint on the client server every time a subscription state is updated. \n\nWe currently have both of these setup to send the request to /apple-notifications, however going forward I think it would be best to have the Android webhook setup to hit /android-notifications, as the behaviour will differ slightly per platform.\n\nBoth Webhooks send the purchase token (in some variation) that the server would have recieved when the user originally purchased the subscription in the post request. We'll then use this to fetch the user with a matching purchase token (a new field may need to be added so we can save just the purchase token as we currently store the entire receipt object). \n\nOnce we've retrieved the correct user, we can then hit the verify endpoints mentioned above to get the up to date subscription state, this should be done every time the Webhook gets fired to ensure we capture events such as cancelations, renewals, refunds etc.\n\nOnce the verify endpoints have been hit, we can update the cancelled property on the subscription based on the response, and also update the expiry date. This ideally should be done every time the webhook is fired, to ensure the expiry date in Joomla matches what the user can see in their respective app store.\n\nIf we are unable to find a user with the token provided, it could be from a legacy customer, as this new system will not work for them as we don't have all the information we need on the user object. In these cases we should save the contents of the post request to a text file as we do currently, this can then be used for resolving any issues and will help with debugging in the future as we will need to update these records manually.\n\nJWT to Bearer Conversion\n=======================\n\nThis is a Node.js script that converts a JKF (JSON Key File) into a JSON Web Token (JWT) into a Bearer token by making a request to the Google OAuth 2.0 API.\n\nPrerequisites\n-------------\n\nBefore running this script, ensure you have the following:\n\n-   Node.js installed on your machine.\n-   A Google Cloud Platform (GCP) service account with the necessary credentials:\n    -   `SERVICE_ACCOUNT_EMAIL`: The email address of the service account.\n    -   `PRIVATE_KEY`: The private key associated with the service account.\n    -   `CLAIM_SET_SCOPE`: The scope(s) of the token.\n\nInstallation\n------------\n\n1.  Clone this repository:\n\n    `git clone https://github.com/charlespalmerbf/google-jwt-to-bearer`\n\n2.  Navigate to the project directory:\n\n    `cd google-jwt-to-bearer`\n\n3.  Install the required dependencies:\n\n    `npm install`\n\n4.  Set up your environment variables by creating a `.env` file and populating it with the required values:\n\n    ```\n        SERVICE_ACCOUNT_EMAIL=your-service-account-email@example.com\n        PRIVATE_KEY=your-service-account-private-key\n        CLAIM_SET_SCOPE=desired-scope\n    ```\n\nUsage\n-----\n\nTo convert a JWT to a Bearer token, execute the following command:\n\n`node jwt.js`\n\nThe script will make a request to the Google OAuth 2.0 API and log the response JSON to the console.\n\nNote: If any errors occur during the execution, they will also be logged to the console.\n\nLicense\n-------\n\nThis project is licensed under the [MIT License](https://opensource.org/license/mit/). Feel free to modify and use it according to your needs.\n\nDisclaimer\n----------\n\nThis script is provided as-is without any warranty. Use it at your own risk.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcharlespalmerbf%2Fgoogle-jwt-to-bearer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcharlespalmerbf%2Fgoogle-jwt-to-bearer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcharlespalmerbf%2Fgoogle-jwt-to-bearer/lists"}