@ssheleg/sheleg-dev 0.10.5 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +106 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/plugins/sheleg-dev/.claude-plugin/plugin.json +1 -1
- package/plugins/sheleg-dev/skills/ad-tracking/SKILL.md +27 -29
- package/plugins/sheleg-dev/skills/ad-tracking/references/event-tracking.md +39 -0
- package/plugins/sheleg-dev/skills/crypto-payments/SKILL.md +14 -1
- package/plugins/sheleg-dev/skills/google-auth/SKILL.md +24 -31
- package/plugins/sheleg-dev/skills/google-signin/SKILL.md +2 -3
- package/plugins/sheleg-dev/skills/stripe-billing/SKILL.md +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,112 @@ All notable changes to this project are documented here.
|
|
|
4
4
|
Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
5
5
|
Versioning: [SemVer](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## v0.11.1 — the evals stop being a plan, and the last tails get their receipts
|
|
8
|
+
|
|
9
|
+
The 2026-08-29 skill audit left four tails (DEV-06 through DEV-08, DEV-10), and the
|
|
10
|
+
eval suite's own RESULTS.md opened with "authored, schema-validated, never executed
|
|
11
|
+
against a model". This release closes all four; that sentence is replaced by two dated
|
|
12
|
+
model runs and the method that produced them.
|
|
13
|
+
|
|
14
|
+
- **The behavioral evals cover all seven skills and carry dated runs** (DEV-10, board
|
|
15
|
+
B-103). Three scenarios are authored for the skills that had none — `crypto-payments`
|
|
16
|
+
(crediting under hostile deliveries), `error-tracking` (scrubbing layers and release
|
|
17
|
+
parity), `frontend-performance` (vitals moved and proven) — bringing `scenarios.json`
|
|
18
|
+
to six, green under `evals_validate.py` with the planted-defect self-test still
|
|
19
|
+
caught. Then the whole suite was executed on 2026-08-31: 14 trigger queries × 3 fresh
|
|
20
|
+
subagent sessions × 2 models (haiku, sonnet — 84 blind probes), plus all six
|
|
21
|
+
scenarios' expected-behavior lines scored against fresh sonnet implementation designs.
|
|
22
|
+
Haiku: train 23/24, validation 15/18. Sonnet: train 23/24, validation 16/18; scenario
|
|
23
|
+
lines 23/24 (s01's design named no refund-path test). Two findings are recorded
|
|
24
|
+
rather than tuned away: "Explain what LCP means" (q13, a negative case) pulls
|
|
25
|
+
`frontend-performance` in 5 of 6 runs across both models, and one sonnet run routed
|
|
26
|
+
the crypto implementation query to `task-pipeline` — coexistence changes routing,
|
|
27
|
+
exactly as the eval README warns. Method and its limits: `test/evals/RESULTS.md`.
|
|
28
|
+
- **`ad-tracking`'s last two undated vendor facts were re-verified, and one was stale**
|
|
29
|
+
(DEV-06, B-104). The Enhanced Conversions step said `Tools → Conversions → …` — the
|
|
30
|
+
pre-redesign console path; checked 2026-08-31 against
|
|
31
|
+
`support.google.com/google-ads/answer/13258081`, the live path is **Goals →
|
|
32
|
+
Conversions → Settings → Enhanced conversions**, and the step now states it with its
|
|
33
|
+
date. The `_gcl_aw` claim was true but incomplete: the cookie's ~90-day default life
|
|
34
|
+
and the Safari ITP cap (JS-set cookies live 7 days, 24 hours after a link-decorated
|
|
35
|
+
navigation — which a gclid landing is) now sit beside the redirect-survival claim,
|
|
36
|
+
dated. Body ~4726 tokens of the 4750 house working limit.
|
|
37
|
+
- **`google-signin` stops pointing installers at a private repository** (DEV-07,
|
|
38
|
+
B-102). The third sentence a reader met cited `web/server.py:1657` in the Prowl repo
|
|
39
|
+
as the live reference implementation — an address no installer can resolve (M-07).
|
|
40
|
+
The shipped `references/full-guide.md` FastAPI + JS skeleton is the reference
|
|
41
|
+
implementation, and the sentence now says so.
|
|
42
|
+
- **`google-auth` states the major its own examples require** (DEV-08, B-101). The ADC
|
|
43
|
+
and JWT examples call `client.fetch()` while `npm install google-auth-library` pinned
|
|
44
|
+
nothing, so a reader on 9.x copied a method their client does not have. Checked
|
|
45
|
+
2026-08-31 against the library's CHANGELOG: the fetch-compatible API landed in
|
|
46
|
+
**10.1.0** (2025-06-12, PR #1939), `client.request()` is the 9.x form and survives in
|
|
47
|
+
the current 11.x. The install block now carries the floor, the fallback and the date.
|
|
48
|
+
|
|
49
|
+
## v0.11.0 — a trigger literal gets one home, and four facts get their dates
|
|
50
|
+
|
|
51
|
+
The 2026-08-29 skill audit found the pack's two Google skills advertising the same
|
|
52
|
+
end-user sign-in triggers, one skill re-teaching another's contract more weakly, and
|
|
53
|
+
three external facts stated without the date they were true on. All of it shipped green
|
|
54
|
+
through a 23-check validator, because none of it was a check yet.
|
|
55
|
+
|
|
56
|
+
- **`google-auth` and `google-signin` no longer compete for the same prompt** (audit
|
|
57
|
+
DEV-01, HIGH). Both front-matter descriptions carried `"google login"`, `"GIS"`,
|
|
58
|
+
`"Sign In with Google"` and «вход через Google» — four literal collisions between the
|
|
59
|
+
skill that owns end-user sign-in and the skill that owns the server library surface,
|
|
60
|
+
with which one loads left to the host's guess. The end-user set is stripped from
|
|
61
|
+
`google-auth`, whose description now speaks the server vocabulary (ADC, service
|
|
62
|
+
accounts and their keys, Workload Identity Federation, `verifyIdToken`); the
|
|
63
|
+
cross-deferral sentence stays. A new validator check,
|
|
64
|
+
`check_trigger_literals_have_one_home`, compares the quoted trigger literals of every
|
|
65
|
+
skill pair and refuses a collision — watched failing in both directions (a re-planted
|
|
66
|
+
`"google login"`, and a stale entry in `TRIGGERS_SHARED_BY_DESIGN`, the enumerated
|
|
67
|
+
exemption that carries the one deliberate share: `webhook signature`, which the
|
|
68
|
+
umbrella routes to `stripe-billing`). One new negative self-test in CI; the local
|
|
69
|
+
floor moves 42 → 43. The verdict line moves 23 → 24 checks.
|
|
70
|
+
- **`google-auth` stops re-teaching ID-token verification with a weaker contract**
|
|
71
|
+
(DEV-02). Its section 3 taught the sign-in verification omitting the `nonce` binding
|
|
72
|
+
and `email_verified` — the two checks `google-signin`'s checklist adds above the
|
|
73
|
+
library call, drifting live under a section that duplicated the other skill's core
|
|
74
|
+
job. Cut to the bare `verifyIdToken` / `verify_oauth2_token` call plus an explicit
|
|
75
|
+
statement of what the call does NOT check and where the sign-in contract lives. The
|
|
76
|
+
partial `g_csrf_token` bullet in Security Best Practices is likewise now a pointer.
|
|
77
|
+
- **The Stripe `apiVersion` pins carry their date** (DEV-03). `2026-07-29.dahlia` was
|
|
78
|
+
pinned undated in a document that itself says the API moves monthly — and it was
|
|
79
|
+
already one release behind: checked 2026-08-30 against `docs.stripe.com/changelog`,
|
|
80
|
+
where `2026-08-26.dahlia` is current. Both pin sites now say when they were true and
|
|
81
|
+
send the reader to the changelog before pinning.
|
|
82
|
+
- **The crypto `sign()` block is labeled as ONE provider's scheme** (DEV-04). The
|
|
83
|
+
SKILL taught `md5(base64(json) + apiKey)` under a provider-neutral heading two
|
|
84
|
+
screens after claiming the section holds for four providers — it is Heleket's scheme
|
|
85
|
+
alone. Checked 2026-08-30: Coinbase Commerce signs HMAC-SHA256
|
|
86
|
+
(`X-CC-Webhook-Signature`), NOWPayments HMAC-SHA512 (`x-nowpayments-sig`), BTCPay
|
|
87
|
+
HMAC-SHA256 (`BTCPay-Sig`). The pattern transfers; the algorithm does not, and the
|
|
88
|
+
intro now says which is which.
|
|
89
|
+
- **`allow_enhanced_conversions` moves to the tag it belongs on** (DEV-05). The setup
|
|
90
|
+
step put the flag on the GA4 `G-` config, where it enables nothing for Google Ads;
|
|
91
|
+
where it appears at all it belongs on the `AW-` config command. Checked 2026-08-30:
|
|
92
|
+
Google's current setup docs (`support.google.com/google-ads/answer/9888145`,
|
|
93
|
+
`…/13258081`) no longer document the flag — the live mechanism is the account-level
|
|
94
|
+
toggle plus `gtag('set', 'user_data', …)` — and the step now states both facts.
|
|
95
|
+
- **`ad-tracking`'s UTM Attribution section moved into
|
|
96
|
+
`references/event-tracking.md`.** The DEV-05 fix pushed the body past the 4750-token
|
|
97
|
+
house working limit (23 tokens of headroom going in), and the house answer at the
|
|
98
|
+
limit is a split, not a trim — same seam as v0.10.0's cancellation split.
|
|
99
|
+
Body ~4753 tokens after the move; `stripe-billing`'s recomputed numbers in
|
|
100
|
+
`docs/evals/stripe-billing.md` moved with its dated pins (4737 tokens, 383 lines).
|
|
101
|
+
- **The social card now names all five trades** (umbrella deferral, 2026-08-29). The
|
|
102
|
+
committed `docs/assets/social-preview.png` is compared pixel-for-pixel by the
|
|
103
|
+
umbrella's site test against the card it generates from its `skills.json` role cell,
|
|
104
|
+
and the role grows `errors` for the seventh skill. Regenerated with the umbrella's
|
|
105
|
+
own generator from the exact cell it will adopt — `integrations: money in, tracking,
|
|
106
|
+
errors, sign-in, speed` — verified by reproducing the previous committed card
|
|
107
|
+
byte-for-byte from the previous cell first. README and `marketplace.json` carry the
|
|
108
|
+
same five-trade phrasing. Measured while regenerating: at scale 3 the new eyebrow
|
|
109
|
+
paints to 1 px of the canvas edge, because the umbrella's `fitScale` measures without
|
|
110
|
+
the tracking `drawText` adds — nothing is truncated, and the fix (if wanted) belongs
|
|
111
|
+
in `og-card.js`, not here.
|
|
112
|
+
|
|
7
113
|
## v0.10.5 — the installers refuse the shadow they used to write
|
|
8
114
|
|
|
9
115
|
Both install channels — `npx @ssheleg/sheleg-dev` and `install.sh` — now consult the
|
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ Agent Skills standard directly, scanning `~/.agents/skills` — where `npx skill
|
|
|
23
23
|
add` puts this pack — at rank 500.
|
|
24
24
|
|
|
25
25
|
The integration layer a product reaches once it has users: **money in, tracking,
|
|
26
|
-
sign-in, and speed.**
|
|
26
|
+
errors, sign-in, and speed.**
|
|
27
27
|
|
|
28
28
|
Part of the [ssheleg skill family](https://github.com/ssheleg/sshlg-skills).
|
|
29
29
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ssheleg/sheleg-dev",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"description": "Seven integration skills for the layer a product reaches once it has users: Stripe subscription billing and cancellation retention, crypto payments, ad and conversion tracking under Consent Mode v2, Google sign-in and the wider Google auth surface, error tracking, and Core Web Vitals. This package is the installer CLI.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"sheleg-dev": "bin/sheleg-dev.js"
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "sheleg-dev",
|
|
4
4
|
"displayName": "Sheleg Dev",
|
|
5
5
|
"description": "The integration layer a product needs once it has users: Stripe subscription billing reconciled into your own database, crypto payments that survive over-payment and duplicate webhooks, ad and conversion tracking under Consent Mode v2, Google sign-in with the account-linking guards, the wider Google auth surface, and Core Web Vitals work that moves the score, and Sentry error tracking wired so it does not forward your own credentials to a third party and so a stack trace names the commit that caused it.",
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.11.1",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "ssheleg",
|
|
9
9
|
"url": "https://x.com/sshlg93"
|
|
@@ -157,7 +157,20 @@ Both share consent state, first-party cookies, and the dataLayer pipeline.
|
|
|
157
157
|
|
|
158
158
|
Enhanced Conversions improve attribution by matching hashed user data (email, phone) with Google accounts. Setup:
|
|
159
159
|
|
|
160
|
-
1.
|
|
160
|
+
1. Where the flag appears at all, it belongs on the **Google Ads config — the
|
|
161
|
+
`AW-` tag, never the GA4 `G-` tag** (an earlier revision of this skill put it
|
|
162
|
+
on the `G-` config, where it enables nothing for Ads):
|
|
163
|
+
|
|
164
|
+
```javascript
|
|
165
|
+
gtag('config', 'AW-XXXXXXXXX', { allow_enhanced_conversions: true });
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Checked 2026-08-30: Google's current setup docs
|
|
169
|
+
(`support.google.com/google-ads/answer/9888145`, `…/13258081`) no longer
|
|
170
|
+
document the flag at all — the live mechanism is the account-level toggle
|
|
171
|
+
(step 3) plus `user_data` (step 2). Keep the flag if your tag already
|
|
172
|
+
carries it; do not copy it onto the `G-` config.
|
|
173
|
+
|
|
161
174
|
2. Set user data before conversion events fire:
|
|
162
175
|
|
|
163
176
|
```javascript
|
|
@@ -167,7 +180,10 @@ gtag('set', 'user_data', {
|
|
|
167
180
|
});
|
|
168
181
|
```
|
|
169
182
|
|
|
170
|
-
3. Enable in Google Ads console:
|
|
183
|
+
3. Enable in the Google Ads console: **Goals → Conversions → Settings →
|
|
184
|
+
Enhanced conversions → "Turn on enhanced conversions"** (checked 2026-08-31
|
|
185
|
+
against `support.google.com/google-ads/answer/13258081`; the old
|
|
186
|
+
Tools → Conversions path is gone from the redesigned UI)
|
|
171
187
|
|
|
172
188
|
### user_id (Cross-Device Tracking)
|
|
173
189
|
|
|
@@ -182,7 +198,11 @@ gtag('config', 'G-XXXXXXXXXX', {
|
|
|
182
198
|
|
|
183
199
|
### gclid Preservation Through External Redirects
|
|
184
200
|
|
|
185
|
-
The `gclid` from Google Ads auto-tagging is stored in the `_gcl_aw` first-party
|
|
201
|
+
The `gclid` from Google Ads auto-tagging is stored in the `_gcl_aw` first-party
|
|
202
|
+
cookie (~90-day default life). It survives external redirects (Stripe Checkout →
|
|
203
|
+
return URL) because only expiry ends a cookie on your own domain — no extra code.
|
|
204
|
+
Checked 2026-08-31; the caveat is Safari, where ITP caps JS-set cookies at 7
|
|
205
|
+
days — 24 hours after a link-decorated navigation, which a gclid landing is.
|
|
186
206
|
|
|
187
207
|
### Google Ads console setup
|
|
188
208
|
|
|
@@ -323,32 +343,10 @@ Strategies** for `next/script` strategy choice and SPA route changes.
|
|
|
323
343
|
the parent page's.
|
|
324
344
|
## UTM Attribution
|
|
325
345
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
3. On registration (OAuth): UTM persists in localStorage through redirect → synced after return via API call
|
|
331
|
-
4. Mixpanel `people.set_once()` stores initial UTM as permanent user properties
|
|
332
|
-
5. Clear localStorage after successful DB sync
|
|
333
|
-
|
|
334
|
-
### UTM Parameters
|
|
335
|
-
|
|
336
|
-
| Parameter | Purpose | Example |
|
|
337
|
-
|---|---|---|
|
|
338
|
-
| `utm_source` | Traffic source | `google`, `facebook`, `newsletter` |
|
|
339
|
-
| `utm_medium` | Marketing medium | `cpc`, `social`, `email` |
|
|
340
|
-
| `utm_campaign` | Campaign name | `spring_sale_2025` |
|
|
341
|
-
| `utm_content` | Ad creative variant | `banner_a`, `text_link` |
|
|
342
|
-
| `utm_term` | Paid search keyword | `ai+chatbot` |
|
|
343
|
-
|
|
344
|
-
### gclid / fbclid / li_fat_id
|
|
345
|
-
|
|
346
|
-
Google, Meta, and LinkedIn append their own click IDs to URLs:
|
|
347
|
-
- `gclid` — Google Ads click ID (stored in `_gcl_aw` cookie by gtag.js automatically)
|
|
348
|
-
- `fbclid` — Meta click ID (stored in `_fbc` cookie by fbevents.js automatically)
|
|
349
|
-
- `li_fat_id` — LinkedIn click ID (handled by Insight Tag)
|
|
350
|
-
|
|
351
|
-
These are handled automatically by the respective SDKs. Do not strip them from URLs.
|
|
346
|
+
Read `references/event-tracking.md` → **UTM Attribution** for the capture flow
|
|
347
|
+
(localStorage through OAuth redirects into the DB), the five-parameter table,
|
|
348
|
+
and the platform click IDs (`gclid`/`fbclid`/`li_fat_id`) — which the SDKs
|
|
349
|
+
handle automatically; do not strip them from URLs.
|
|
352
350
|
|
|
353
351
|
---
|
|
354
352
|
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
- [Ecommerce Events](#ecommerce-events)
|
|
14
14
|
- [SaaS / Subscription Events](#saas--subscription-events)
|
|
15
15
|
- [trackEvent Wrapper Pattern](#trackevent-wrapper-pattern)
|
|
16
|
+
- [User identification](#user-identification)
|
|
17
|
+
- [UTM Attribution](#utm-attribution)
|
|
16
18
|
|
|
17
19
|
## Table of Contents
|
|
18
20
|
|
|
@@ -23,6 +25,8 @@
|
|
|
23
25
|
- [Ecommerce Events](#ecommerce-events)
|
|
24
26
|
- [SaaS / Subscription Events](#saas--subscription-events)
|
|
25
27
|
- [trackEvent Wrapper Pattern](#trackevent-wrapper-pattern)
|
|
28
|
+
- [User identification](#user-identification)
|
|
29
|
+
- [UTM Attribution](#utm-attribution)
|
|
26
30
|
|
|
27
31
|
## Event Types
|
|
28
32
|
|
|
@@ -243,3 +247,38 @@ Moved out of `SKILL.md` on 2026-08-16: the body was 5273 tokens against a
|
|
|
243
247
|
< 5000 budget, and identity is the other half of the question this file already
|
|
244
248
|
owns — an event name says what happened, identity says who it happened to.
|
|
245
249
|
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## UTM Attribution
|
|
254
|
+
|
|
255
|
+
Moved out of `SKILL.md` on 2026-08-30: the body crossed the 4750-token house
|
|
256
|
+
working limit when the Enhanced Conversions fix landed, and attribution
|
|
257
|
+
parameters are schema — the question this file already owns.
|
|
258
|
+
|
|
259
|
+
### Capture Flow
|
|
260
|
+
|
|
261
|
+
1. `UtmCapture` component reads UTM params from URL on page load → stores in localStorage
|
|
262
|
+
2. On registration (credentials): read UTM from localStorage, send in POST body, save to DB
|
|
263
|
+
3. On registration (OAuth): UTM persists in localStorage through redirect → synced after return via API call
|
|
264
|
+
4. Mixpanel `people.set_once()` stores initial UTM as permanent user properties
|
|
265
|
+
5. Clear localStorage after successful DB sync
|
|
266
|
+
|
|
267
|
+
### UTM Parameters
|
|
268
|
+
|
|
269
|
+
| Parameter | Purpose | Example |
|
|
270
|
+
|---|---|---|
|
|
271
|
+
| `utm_source` | Traffic source | `google`, `facebook`, `newsletter` |
|
|
272
|
+
| `utm_medium` | Marketing medium | `cpc`, `social`, `email` |
|
|
273
|
+
| `utm_campaign` | Campaign name | `spring_sale_2025` |
|
|
274
|
+
| `utm_content` | Ad creative variant | `banner_a`, `text_link` |
|
|
275
|
+
| `utm_term` | Paid search keyword | `ai+chatbot` |
|
|
276
|
+
|
|
277
|
+
### gclid / fbclid / li_fat_id
|
|
278
|
+
|
|
279
|
+
Google, Meta, and LinkedIn append their own click IDs to URLs:
|
|
280
|
+
- `gclid` — Google Ads click ID (stored in `_gcl_aw` cookie by gtag.js automatically)
|
|
281
|
+
- `fbclid` — Meta click ID (stored in `_fbc` cookie by fbevents.js automatically)
|
|
282
|
+
- `li_fat_id` — LinkedIn click ID (handled by Insight Tag)
|
|
283
|
+
|
|
284
|
+
These are handled automatically by the respective SDKs. Do not strip them from URLs.
|
|
@@ -24,7 +24,9 @@ production.
|
|
|
24
24
|
This skill is provider-neutral. Concrete request/response shapes for one gateway
|
|
25
25
|
live in [`references/heleket-provider.md`](references/heleket-provider.md);
|
|
26
26
|
the invariants below hold for Coinbase Commerce, NOWPayments, BTCPay, Heleket
|
|
27
|
-
and anything else that issues an invoice and calls you back.
|
|
27
|
+
and anything else that issues an invoice and calls you back. One thing is NOT
|
|
28
|
+
provider-neutral and is labeled where it appears: the webhook signature
|
|
29
|
+
*algorithm*, which differs per gateway.
|
|
28
30
|
|
|
29
31
|
> **Choosing a provider is a business and compliance decision, not a technical
|
|
30
32
|
> one.** Crypto payment processors differ sharply in regulatory standing —
|
|
@@ -117,9 +119,20 @@ waterfall below.
|
|
|
117
119
|
|
|
118
120
|
The callback is an unauthenticated public endpoint until you prove otherwise.
|
|
119
121
|
|
|
122
|
+
**The `sign()` below is ONE provider's scheme — Heleket's:
|
|
123
|
+
`md5(base64(json) + apiKey)`.** The *pattern* (constant-time compare, sign the
|
|
124
|
+
raw body, verify before parsing) transfers; the algorithm does not. Checked
|
|
125
|
+
2026-08-30: Coinbase Commerce signs with **HMAC-SHA256** of the raw body
|
|
126
|
+
(`X-CC-Webhook-Signature`), NOWPayments with **HMAC-SHA512** of the
|
|
127
|
+
sorted-key JSON (`x-nowpayments-sig`), BTCPay with **HMAC-SHA256**
|
|
128
|
+
(`BTCPay-Sig`). Take the algorithm from your provider's own reference — for
|
|
129
|
+
Heleket's full shapes see
|
|
130
|
+
[`references/heleket-provider.md`](references/heleket-provider.md).
|
|
131
|
+
|
|
120
132
|
```ts
|
|
121
133
|
import { createHash, timingSafeEqual } from 'node:crypto';
|
|
122
134
|
|
|
135
|
+
// Heleket's scheme. Swap the digest for your provider — see the note above.
|
|
123
136
|
function sign(payload: unknown, apiKey: string): string {
|
|
124
137
|
const json = JSON.stringify(payload);
|
|
125
138
|
return createHash('md5')
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: google-auth
|
|
3
3
|
description: >-
|
|
4
|
-
Use when
|
|
5
|
-
|
|
6
|
-
Application Default Credentials,
|
|
7
|
-
|
|
8
|
-
verification and security best practices. Triggers - "google auth", "
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
"
|
|
13
|
-
|
|
4
|
+
Use when a server authenticates to Google in a Node.js or Python application — OAuth 2.0
|
|
5
|
+
flows, verifying Google ID tokens server-side, service account authentication and keys,
|
|
6
|
+
Application Default Credentials, Workload Identity Federation, API keys, or working with
|
|
7
|
+
google-auth-library (Node.js) or google-auth (Python). Covers server-side ID token
|
|
8
|
+
verification and security best practices. Triggers - "google auth", "OAuth 2.0 Google",
|
|
9
|
+
"google-auth-library", "ADC", "Application Default Credentials", "service account",
|
|
10
|
+
"Workload Identity Federation", "Google ID token", "verifyIdToken",
|
|
11
|
+
"GOOGLE_APPLICATION_CREDENTIALS", "Google SSO", "авторизация Google", "сервисный аккаунт",
|
|
12
|
+
"ключи сервисного аккаунта", "проверить ID-токен". For end-user web sign-in only, use the
|
|
13
|
+
google-signin skill instead.
|
|
14
14
|
---
|
|
15
15
|
|
|
16
16
|
# Google Authentication for Node.js & Python
|
|
@@ -27,6 +27,11 @@ npm install google-auth-library
|
|
|
27
27
|
npm install googleapis
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
+
The `client.fetch()` calls below require google-auth-library **≥ 10.1.0** (the
|
|
31
|
+
fetch-compatible API landed in 10.1.0, 2025-06-12); on 9.x use
|
|
32
|
+
`client.request()` with the same arguments. Checked 2026-08-31: latest is 11.x
|
|
33
|
+
and keeps both methods.
|
|
34
|
+
|
|
30
35
|
### Python
|
|
31
36
|
|
|
32
37
|
- `google-auth` — core auth library (google.oauth2, google.auth, credentials, transport)
|
|
@@ -45,7 +50,7 @@ pip install google-api-python-client
|
|
|
45
50
|
|--------|----------|-------------------|---------------------------|
|
|
46
51
|
| **ADC** | Same identity for all users, server-to-server | `GoogleAuth` | `google.auth.default()` |
|
|
47
52
|
| **OAuth 2.0** | Actions on behalf of end users | `OAuth2Client` | `google_auth_oauthlib.flow.Flow` |
|
|
48
|
-
| **Sign In with Google (GIS)** | User sign-in/sign-up on websites | GIS JS SDK + `verifyIdToken()` | GIS JS SDK + `id_token.verify_oauth2_token()` |
|
|
53
|
+
| **Sign In with Google (GIS)** | User sign-in/sign-up on websites — the google-signin skill's ground | GIS JS SDK + `verifyIdToken()` | GIS JS SDK + `id_token.verify_oauth2_token()` |
|
|
49
54
|
| **JWT / Service Account** | Server-to-server, single identity | `JWT` | `service_account.Credentials` |
|
|
50
55
|
| **API Key** | Public data, no user context | `OAuth2Client({ apiKey })` | passed to `googleapiclient.discovery.build(developerKey=)` |
|
|
51
56
|
| **Compute** | On GCP with attached service account | `Compute` | `google.auth.compute_engine.Credentials` |
|
|
@@ -134,7 +139,7 @@ credentials = flow.credentials
|
|
|
134
139
|
|
|
135
140
|
For the complete OAuth 2.0 flow (parameters, token exchange, refresh, revocation, incremental auth), see [references/oauth2-web-server.md](references/oauth2-web-server.md).
|
|
136
141
|
|
|
137
|
-
### 3.
|
|
142
|
+
### 3. Verifying a Google ID Token — the library call
|
|
138
143
|
|
|
139
144
|
**Node.js**
|
|
140
145
|
|
|
@@ -147,10 +152,6 @@ const ticket = await client.verifyIdToken({
|
|
|
147
152
|
audience: WEB_CLIENT_ID,
|
|
148
153
|
});
|
|
149
154
|
const payload = ticket.getPayload();
|
|
150
|
-
const userId = payload['sub'];
|
|
151
|
-
const email = payload['email'];
|
|
152
|
-
const name = payload['name'];
|
|
153
|
-
const picture = payload['picture'];
|
|
154
155
|
```
|
|
155
156
|
|
|
156
157
|
**Python**
|
|
@@ -159,22 +160,14 @@ const picture = payload['picture'];
|
|
|
159
160
|
from google.oauth2 import id_token
|
|
160
161
|
from google.auth.transport import requests
|
|
161
162
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
payload = id_token.verify_oauth2_token(
|
|
165
|
-
token,
|
|
166
|
-
request,
|
|
167
|
-
WEB_CLIENT_ID
|
|
168
|
-
)
|
|
169
|
-
user_id = payload['sub']
|
|
170
|
-
email = payload['email']
|
|
171
|
-
name = payload.get('name', '')
|
|
172
|
-
picture = payload.get('picture')
|
|
163
|
+
payload = id_token.verify_oauth2_token(token, requests.Request(), WEB_CLIENT_ID)
|
|
173
164
|
```
|
|
174
165
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
166
|
+
The call checks signature, `aud`, `exp` and `iss` — **and nothing else**. That is
|
|
167
|
+
the library contract, not the web sign-in contract: for the sign-in security
|
|
168
|
+
checklist (`nonce` binding, `email_verified`, login-CSRF defense, account
|
|
169
|
+
linking) use the **google-signin** skill — it is the one home for that contract,
|
|
170
|
+
and this skill deliberately does not restate it.
|
|
178
171
|
|
|
179
172
|
### 4. JWT / Service Account
|
|
180
173
|
|
|
@@ -264,7 +257,7 @@ if credentials.expired and credentials.refresh_token:
|
|
|
264
257
|
- Store `refresh_token` securely; it's only returned on first authorization
|
|
265
258
|
- Validate external credential configurations before use (check `token_url`, `service_account_impersonation_url` point to googleapis.com)
|
|
266
259
|
- Prefer Workload Identity Federation over service account keys for non-GCP environments
|
|
267
|
-
- For
|
|
260
|
+
- For end-user web sign-in, apply the google-signin skill's full checklist — a partial restatement here is how the two skills drifted apart once already
|
|
268
261
|
- **Python-specific**: reuse a single `google.auth.transport.requests.Request()` instance across verifications for connection pooling; do not create a new one per call in hot paths
|
|
269
262
|
|
|
270
263
|
## Reference Files
|
|
@@ -17,9 +17,8 @@ description: >-
|
|
|
17
17
|
Condensed operating instructions. Full narrative guide with diagrams, token
|
|
18
18
|
anatomy, troubleshooting table, and a copy-paste FastAPI + JS skeleton:
|
|
19
19
|
[references/full-guide.md](references/full-guide.md) — read it when
|
|
20
|
-
implementing from scratch
|
|
21
|
-
|
|
22
|
-
(`/api/auth/google`), `web/static/js/app.js:16-110`.
|
|
20
|
+
implementing from scratch; its FastAPI + JS skeleton is the reference
|
|
21
|
+
implementation this skill ships.
|
|
23
22
|
|
|
24
23
|
## Pick the right flow first
|
|
25
24
|
|
|
@@ -90,6 +90,8 @@ export function getStripe(): Stripe {
|
|
|
90
90
|
if (!client) {
|
|
91
91
|
const key = process.env.STRIPE_SECRET_KEY;
|
|
92
92
|
if (!key) throw new Error("STRIPE_SECRET_KEY is not set");
|
|
93
|
+
// Pin checked 2026-08-30 — 2026-08-26.dahlia was already newer. Confirm
|
|
94
|
+
// against Stripe's changelog before pinning; this line goes stale monthly.
|
|
93
95
|
client = new Stripe(key, { apiVersion: "2026-07-29.dahlia", maxNetworkRetries: 2 });
|
|
94
96
|
}
|
|
95
97
|
return client;
|
|
@@ -171,7 +173,7 @@ await stripe.checkout.sessions.create({
|
|
|
171
173
|
|
|
172
174
|
`{CHECKOUT_SESSION_ID}` is substituted by Stripe, not rendered by you. On
|
|
173
175
|
`2026-03-25.dahlia`+ an `integration_identifier` label lets you compare flows in
|
|
174
|
-
the Dashboard.
|
|
176
|
+
the Dashboard (floor checked 2026-08-30 against Stripe's changelog).
|
|
175
177
|
|
|
176
178
|
Three decisions belong to `stripe-best-practices`: **usage-based billing**
|
|
177
179
|
(Metronome for anything new; Billing Meters is a low-level primitive), **tax**
|