Skip to content

Commit 6f38c30

Browse files
committed
fix(coinpay): correct OAuth per coinpayportal docs
- endpoints under /api/oauth/{authorize,token,userinfo} - valid scopes: openid profile email did wallet:read (payments:x402 isn't a scope) - token exchange uses HTTP Basic auth (client_id:client_secret) - use userinfo email + email_verified
1 parent 5a3acd5 commit 6f38c30

3 files changed

Lines changed: 17 additions & 12 deletions

File tree

.env.example

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,5 @@ DASHSCOPE_API_KEY= # Qwen (Alibaba); QWEN_API_KEY also accepted
3333
COINPAY_CLIENT_ID=
3434
COINPAY_REDIRECT_URI=tronbrowser://oauth/coinpay
3535
# Override only for self-hosted CoinPay:
36-
# COINPAY_AUTHORIZE_URL=https://coinpayportal.com/oauth/authorize
37-
# COINPAY_TOKEN_URL=https://coinpayportal.com/oauth/token
36+
# COINPAY_AUTHORIZE_URL=https://coinpayportal.com/api/oauth/authorize
37+
# COINPAY_TOKEN_URL=https://coinpayportal.com/api/oauth/token

packages/auth/src/coinpay-oauth.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ import {
1515
} from './oauth.js';
1616

1717
export const COINPAY_DEFAULTS = {
18-
authorizeUrl: 'https://coinpayportal.com/oauth/authorize',
19-
tokenUrl: 'https://coinpayportal.com/oauth/token',
18+
authorizeUrl: 'https://coinpayportal.com/api/oauth/authorize',
19+
tokenUrl: 'https://coinpayportal.com/api/oauth/token',
2020
/** Scopes needed to read wallet addresses and authorize x402 payments. */
21-
scopes: ['wallet:read', 'payments:x402'],
21+
scopes: ['openid', 'profile', 'email', 'did', 'wallet:read'],
2222
} as const;
2323

2424
export interface CoinPayOAuthConfig {

services/api/src/index.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ const CP = {
1414
clientId: process.env.COINPAY_CLIENT_ID || '',
1515
clientSecret: process.env.COINPAY_CLIENT_SECRET || '',
1616
redirectUri: process.env.COINPAY_REDIRECT_URI || 'https://tronbrowser.com/api/auth/coinpay/callback',
17-
authorizeUrl: process.env.COINPAY_AUTHORIZE_URL || 'https://coinpayportal.com/oauth/authorize',
18-
tokenUrl: process.env.COINPAY_TOKEN_URL || 'https://coinpayportal.com/oauth/token',
19-
userinfoUrl: process.env.COINPAY_USERINFO_URL || 'https://coinpayportal.com/oauth/userinfo',
20-
scopes: ['wallet:read', 'payments:x402'],
17+
authorizeUrl: process.env.COINPAY_AUTHORIZE_URL || 'https://coinpayportal.com/api/oauth/authorize',
18+
tokenUrl: process.env.COINPAY_TOKEN_URL || 'https://coinpayportal.com/api/oauth/token',
19+
userinfoUrl: process.env.COINPAY_USERINFO_URL || 'https://coinpayportal.com/api/oauth/userinfo',
20+
scopes: (process.env.COINPAY_SCOPES || 'openid profile email did wallet:read').split(/\s+/).filter(Boolean),
2121
};
2222
const APP_URL = process.env.APP_URL || 'https://tronbrowser.dev';
2323

@@ -67,9 +67,13 @@ app.get('/api/auth/coinpay/callback', async (c) => {
6767
if (!code || state !== getCookie(c, 'cp_state')) return c.text('invalid oauth state', 400);
6868
const redirect = getCookie(c, 'cp_redirect') || '';
6969

70+
const basic = Buffer.from(`${CP.clientId}:${CP.clientSecret}`).toString('base64');
7071
const tokRes = await fetch(CP.tokenUrl, {
7172
method: 'POST',
72-
headers: { 'content-type': 'application/x-www-form-urlencoded' },
73+
headers: {
74+
'content-type': 'application/x-www-form-urlencoded',
75+
authorization: `Basic ${basic}`,
76+
},
7377
body: new URLSearchParams({
7478
grant_type: 'authorization_code', code, redirect_uri: CP.redirectUri,
7579
client_id: CP.clientId, client_secret: CP.clientSecret,
@@ -86,11 +90,12 @@ app.get('/api/auth/coinpay/callback', async (c) => {
8690
const sub = info.sub || info.id || tok.sub;
8791
if (!sub) return c.text('coinpay userinfo missing subject', 502);
8892

93+
const emailVerified = info.email_verified ?? !!info.email;
8994
let user = await userByCoinpaySub(String(sub));
9095
if (!user) {
9196
const id = uuid();
92-
await createUser({ id, authMethod: 'coinpay', coinpaySub: String(sub), email: info.email ?? null, emailVerified: !!info.email });
93-
user = { id, auth_method: 'coinpay', coinpay_sub: String(sub), email: info.email ?? null, email_verified: info.email ? 1 : 0 };
97+
await createUser({ id, authMethod: 'coinpay', coinpaySub: String(sub), email: info.email ?? null, emailVerified });
98+
user = { id, auth_method: 'coinpay', coinpay_sub: String(sub), email: info.email ?? null, email_verified: emailVerified ? 1 : 0 };
9499
}
95100
deleteCookie(c, 'cp_state'); deleteCookie(c, 'cp_redirect');
96101
const r = await startSession(c, user.id, redirect);

0 commit comments

Comments
 (0)