本帖最后由 riyad 于 2025-2-21 19:27 编辑
/ C& Q! G$ x ^+ J3 b9 J0 _
8 ]/ _1 y6 E0 u3 F. g本文提供了不同编程语言的代码示例,说明开发人员如何使用 Telegram 初始数据授权用户。 0 ]) P: M$ N0 v$ l5 ?% s
客户端首先,需要从客户端向服务器传输初始数据开始。 我们可以使用此代码 : - import { retrieveLaunchParams } from '@telegram-apps/sdk';8 _& d# ~( q5 T% f
5 h' \" v5 x L3 j- const { initDataRaw } = retrieveLaunchParams();
& \7 F5 C' m7 D4 G3 D
7 [/ ]1 e) z# F% e! a7 L- fetch('https://example.com/api', {
' B7 E4 k7 g/ d0 w' }( b U7 O8 w" e" B - method: 'POST',
" [2 |5 z& q5 N$ y( X - headers: {
( e2 A+ ]% t: D* f0 g6 Z - Authorization: `tma ${initDataRaw}`$ n' U# ?1 p" B6 g+ [
- },
+ t/ S; [/ K8 C" t9 N$ g) P - });
复制代码我们使用 https://example.com/api URL 向假想的服务器发送请求。 该请求使用 HTTP POST 方法(你可以使用任何你想要的方法),并附加 Authorization(授权)标头,这在这里是最重要的。 表示一个字符串,包含由空格分割的两个部分。 第一个描述授权方法(在 的情况下,我们的服务器将支持其他几种方法),第二个包含授权数据。 就 Telegram Mini Apps 而言,第二部分是原始 init 数据。 2 c1 ~! \- O* Z8 ?
服务器端现在,当 init 数据传输到服务器端时,我们应该创建一个简单的 HTTP 服务器,使用这些数据并 授权用户。 Node.jsNode.js 示例使用 express 处理 HTTP 请求。 - import { validate, parse, type InitDataParsed } from '@telegram-apps/init-data-node';
$ @2 T# i [" C/ D# O - import express, {/ j" R' R& ~0 {- A# B) ]' i
- type ErrorRequestHandler,9 d5 Q+ @/ I5 d% X6 \
- type RequestHandler,
& G. p3 C- e8 i0 w1 E - type Response,$ W% _7 N. d! X5 P/ z# `' @
- } from 'express'; P; G) s% T5 e% V* p
- / \& e/ u& H( e. v+ b
- /**
$ z$ n: r; _$ o+ c. [' f7 U - * Sets init data in the specified Response object.0 E* {- V: w" d: s9 M
- * @param res - Response object.
6 x# ~) g) V$ ^; h - * @param initData - init data.6 U( A; T1 I; z* f( t) M
- */* v0 c0 i! y% q7 t
- function setInitData(res: Response, initData: InitDataParsed): void {
" ~6 E2 {5 g7 g6 l9 l" N - res.locals.initData = initData;
1 ~- n7 Y* ?- m4 U3 z1 j* q2 _ - }, n1 r$ m6 x1 \7 w) a
- 4 _# N0 i6 M% v0 S
- /**
/ h/ h% r4 E4 h% W - * Extracts init data from the Response object.
' Q4 Y0 y& S. o: I: |- L6 E/ f - * @param res - Response object.+ Z7 e$ J/ V4 b9 n2 {) |
- * @returns Init data stored in the Response object. Can return undefined in case,
: l! a, D( S9 m$ R& }5 K1 I - * the client is not authorized.
( |9 V) d# V ` - */
) U; j" a1 N. m9 P, D4 C5 x4 |9 g( X - function getInitData(res: Response): InitDataParsed | undefined {* K3 N4 ~ |3 A: n# e
- return res.locals.initData;
+ Y8 q2 ~$ h+ I5 p6 |- m - }
" E5 W4 V* R$ G' G0 @$ w0 N: p
$ G6 h! o) t, ~4 m- /**6 Z2 G2 s5 a9 a9 F
- * Middleware which authorizes the external client.7 n+ U0 q" x7 z3 q1 b
- * @param req - Request object.
1 l2 \" f# u* E- H% u7 [ - * @param res - Response object.! `. ^- D' J# x* T1 ]; q
- * @param next - function to call the next middleware.
4 K; Q/ w7 }% _7 x; y0 g6 N$ [ - */; S- V$ V9 A! E
- const authMiddleware: RequestHandler = (req, res, next) => {
; o: L6 }* F+ C' F, \ - // We expect passing init data in the Authorization header in the following format:; x7 ~; s" N# n. H5 T% t9 n
- // <auth-type> <auth-data>1 f, b% p5 Z. u3 ]2 v
- // <auth-type> must be "tma", and <auth-data> is Telegram Mini Apps init data.
- i% t# K: W) {* q/ y$ E - const [authType, authData = ''] = (req.header('authorization') || '').split(' ');
5 \5 s) c8 M" a2 \6 k; F3 e - ( B6 N" A$ z2 o J2 ?$ [
- switch (authType) {6 x5 k* x% Z1 _* `% c& a
- case 'tma':. T5 E+ R/ X! t# d* x
- try {
4 r6 P; u0 G" D q - // Validate init data.
, T$ c* u! N5 b' Z" i) K; m- ] - validate(authData, token, {
* h2 ~* V1 X. a1 ^, L- m' J8 Y - // We consider init data sign valid for 1 hour from their creation moment.( J$ o# c! d% N& Y4 ?
- expiresIn: 3600,) a$ t+ j8 r$ q4 L. z6 K& E. V
- });
0 R- W9 T8 W; m$ r, v6 V& R0 Q - / Y; d+ i) e! z. Y1 p5 A1 y
- // Parse init data. We will surely need it in the future.; Y2 a7 K- @( u2 w/ N4 @
- setInitData(res, parse(authData));
1 Q1 C/ S5 y! S& J. k1 c9 M - return next();
' {: S# Y; ?4 V5 f0 F- M) r! e" W - } catch (e) {
) ^% @ R; R9 K2 O# | - return next(e);
( k& l$ }/ n+ o; F) g - }3 M! M6 \/ m, U, `! H( _
- // ... other authorization methods.
6 k; |* L: o" h. J: F - default:. P/ S! U9 H0 M0 M
- return next(new Error('Unauthorized'));! C1 @- c) C, ]& u- L
- }
2 }4 [# W+ Z# H: y - };! f( i1 @+ @+ r5 h5 a" S% [* P
7 ?5 r* O* N; d$ u5 O& n! B1 K- /**: v# y$ o- g z3 H
- * Middleware which shows the user init data.$ c* v" |( A% ~* C% j
- * @param _req
* V- c/ a4 o1 S# c - * @param res - Response object.4 {6 C8 u) o% j& t5 G( Q
- * @param next - function to call the next middleware.4 d, P$ Q) n* {2 L9 a: [! x, W6 ^# {
- */
: i- k3 C" o$ k' `% B - const showInitDataMiddleware: RequestHandler = (_req, res, next) => {! \& a1 f8 T D+ }0 m! R9 |
- const initData = getInitData(res);
' x7 i4 l8 g6 S" ] - if (!initData) {
1 U, z2 G* p" p# R$ ]: w - return next(new Error('Cant display init data as long as it was not found'));
T1 |( ]# U# r5 c6 @. I4 V6 O6 | - }
; ]5 a. i6 S* N+ D- i - res.json(initData);: \6 C+ n& P' J# r' @+ D/ k
- };9 } \- ?+ C6 Z1 Q1 L
- # n0 W9 {' \" |* u0 V( u3 a5 y
- /**
|1 t9 U5 K, u) N0 D3 g - * Middleware which displays the user init data.
2 y% N; {, i% S - * @param err - handled error.: N9 d" ^) y \7 ?0 H
- * @param _req' ?2 w) k) J5 S) D1 E1 Q
- * @param res - Response object.6 u: J8 Q7 ]1 O5 ~4 J! ?
- */
2 G; ]) d' P7 l1 a3 I - const defaultErrorMiddleware: ErrorRequestHandler = (err, _req, res) => {
: T; Y( C8 a8 @6 A - res.status(500).json({' `9 X) ~) I3 F+ o8 x, n
- error: err.message,6 e) M, }! U- Q
- });
- i5 ^% D/ M2 Q - };
9 I. q/ {+ M* M% [& V8 h" W - ' I/ U* v% q/ a1 O* c% ~% }/ N
- // Your secret bot token.
2 v8 C0 [7 ~1 F, j9 x/ u& Z3 R - const token = '1234567890:ABC';
( ^, L) Q$ r: M8 W4 Q9 p
( ^3 [0 O D% X' ]6 g V; X- // Create an Express applet and start listening to port 3000.
( V& c% \' X# S! f1 H7 h; ~ - const app = express();! s% O' v, r1 m- \( d
- ! o" Y& {3 G# r* P" u
- app.use(authMiddleware);
. J' i* D$ q# I6 Y - app.get('/', showInitDataMiddleware);. o3 k$ |& R* ~# J+ O
- app.use(defaultErrorMiddleware);: ~0 z9 A7 W# D- u/ H! s' c
' B; T& A8 X% E2 e# Z" q+ C- app.listen(3000);- y \+ `) R7 F& s* S1 l
- * ?- X5 z. G* `9 Y; |# M( j
- // After the HTTP server was launched, try sending an HTTP GET request to the URL : W9 q* C! J+ N1 ^
- // http://localhost:3000/ with an Authorization header containing data in the required format.
复制代码 8 o8 n# X. u, Q s$ _
GoLang[color=var(--vp-c-brand-1)]GoLang 示例使用 [color=var(--vp-c-brand-1)]gin 处理 HTTP 请求。 - package main) N% v t; L2 x
; G i* N9 N. f& p+ j0 J- import (
7 t. x2 i6 S1 Z: I6 ^ - "context"
0 x- }" I& }4 w7 ^ - "strings"+ v0 E2 b2 k) s/ x. {
- "time"
2 o6 y t q& c8 A+ k - , ]& S0 p q9 h: P! F5 `4 A
- "github.com/gin-gonic/gin", F' E9 {( R; R, K* O d) _# d" \
- initdata "github.com/telegram-mini-apps/init-data-golang"( S1 V; [3 E0 {1 L9 s+ g& Z
- )
8 N7 G+ @% T! R p( s
6 `% u" x2 c4 h' _- type contextKey string
9 H3 Y+ |# f$ T; h$ q* Q* i$ m- D
& x# V0 L* H3 D3 L/ x K- const (0 l ]4 N0 M2 P s. z3 m
- _initDataKey contextKey = "init-data"2 R0 e' r5 h v& v
- )
( z9 M0 ^5 X& F/ _
0 t1 h g/ M$ F" v! p) l3 A( h- // Returns new context with specified init data.2 b8 Y' S n3 H3 a X5 i
- func withInitData(ctx context.Context, initData initdata.InitData) context.Context {
7 ~% z4 ?! I4 ]% I9 J - return context.WithValue(ctx, _initDataKey, initData): G7 l G* p* N! j5 h* c
- }( _ P5 H( j6 `2 q* I, ]
6 ~, K% t6 \; d- A# A7 n; T- // Returns the init data from the specified context.* F) ^9 a! z1 o! {9 Y, m$ _
- func ctxInitData(ctx context.Context) (initdata.InitData, bool) {
( r: S, K# T) c* R- E" Y; t& k - initData, ok := ctx.Value(_initDataKey).(initdata.InitData)
# s y# c7 }7 @6 U9 o& D4 f% _7 p - return initData, ok
k* ?4 h; T1 f' k - }
6 A& U$ O/ W" d1 B - 1 `) d6 r: B1 ]
- // Middleware which authorizes the external client.3 F. I2 u ]- u3 O4 R- c K
- func authMiddleware(token string) gin.HandlerFunc {+ m2 k' J+ Y& N* |
- return func(context *gin.Context) {
. P8 j9 t$ u4 u0 ^! V - // We expect passing init data in the Authorization header in the following format:, ~3 f$ p, t9 P5 G: j( A
- // <auth-type> <auth-data>
% ^* _% ^3 @( K7 l; ` - // <auth-type> must be "tma", and <auth-data> is Telegram Mini Apps init data.3 _( X. o! A2 M: y5 ~2 k# o/ |
- authParts := strings.Split(context.GetHeader("authorization"), " ")
2 u& b& d3 i# Y4 P; o8 z - if len(authParts) != 2 {
" a: Z# p0 I$ {* x& E* M8 k - context.AbortWithStatusJSON(401, map[string]any{
4 ]- J4 u$ U0 v& a; W - "message": "Unauthorized",& B* P& O/ ?6 a7 s* e. s1 h. o
- })
F/ Q: J! Y* G( Z4 g9 M* a5 x - return. b" i) j I9 D& J8 X8 e& j, R, ^
- }
5 g- o$ l% {' a9 o4 ^( o9 W - 4 [4 z. k8 W' c! M7 s9 h
- authType := authParts[0]
% Q9 _+ j1 M0 `( K2 v, T - authData := authParts[1]* Q6 b$ k; \. r9 b
- + r7 e7 X' K! S4 L$ d' K" J
- switch authType {% n9 {: t7 T; X4 t1 T. T
- case "tma":) M( @5 n& i( j" j8 [$ r4 r E, K
- // Validate init data. We consider init data sign valid for 1 hour from their
0 n$ x0 \. l3 f6 U; V - // creation moment.
3 _3 c4 L5 ?+ M: N' y - if err := initdata.Validate(authData, token, time.Hour); err != nil {' f7 ^7 t, _! T. b) r% \4 T
- context.AbortWithStatusJSON(401, map[string]any{& \1 `6 x7 l1 ]6 J3 c
- "message": err.Error(),
* l- A- R* A) k9 [3 ]5 \ - })+ E" O$ l' E! X, x) t
- return7 y- F! _1 w; U. l- v3 l
- }- y. `6 }2 \( k5 s
- % g8 L4 f& H# G; H
- // Parse init data. We will surely need it in the future.
% O8 A8 s7 {, q# B/ ?4 Z - initData, err := initdata.Parse(authData)
& \6 E2 u2 q) E! V/ A% W1 } - if err != nil {
7 {$ M& V* e, o2 Y1 J - context.AbortWithStatusJSON(500, map[string]any{; A5 J4 a8 q( Z. C, \
- "message": err.Error(),1 S; w# }9 c+ i
- })
1 n3 C- S2 C+ A$ m3 [3 n1 H - return
, h) z6 S/ V( X9 b3 R8 O8 `1 O - }
% E, _; q7 c* T+ D n
4 v( v- c& q0 K/ b5 ~ x2 r, k- context.Request = context.Request.WithContext(
' {9 p! \: A9 R! T% U - withInitData(context.Request.Context(), initData),8 D* F. {% Y3 y
- )
+ `8 W9 U' h# S5 ~4 a) Y+ A! [ - }( D' O0 p+ l, I+ C: A
- }
' t2 m) Y) q: I: u* w5 s - }' G& a& t, L7 W( ]; ?3 @4 R
( `4 l# P# Y. B+ I5 X0 W+ U6 A4 ]- // Middleware which shows the user init data.
3 U Z! y0 y# f1 `. F# S6 T& J/ K - func showInitDataMiddleware(context *gin.Context) {
, D0 V+ M1 m2 I; }9 G - initData, ok := ctxInitData(context.Request.Context())
. h, }- h- @4 b' O8 J1 S$ n - if !ok {2 S6 M& _" Z6 ~2 P/ G2 F4 F
- context.AbortWithStatusJSON(401, map[string]any{
3 r: j9 E+ V5 F9 p1 P% T2 X - "message": "Init data not found",
# \* ~( ~, x$ Y4 P/ ?. q - })
+ k: L$ P2 y/ X. y7 W' A% B) S' l - return, U6 V9 H8 |, l& @6 f9 _2 O
- }
! H$ X8 z% p6 @3 Z$ d% T - " s2 N( y6 t# g0 J3 y8 T0 f
- context.JSON(200, initData)3 B- k$ m+ ?9 m& L; d3 p
- }* J7 B- e$ ?" |% z& q
0 E3 ?1 t. j- D1 v+ w- func main() {- X' Y: J6 U- v* ^9 H6 o
- // Your secret bot token.& Q( q3 F! d" n1 r9 K" u
- token := "1234567890:ABC"
9 e! h6 n: ?# j5 Z9 n" p; P - + S" d, {+ u" I& D
- r := gin.New()
+ H0 f. z4 w2 D: C/ ]1 Y! o - # g( v/ v- R5 B0 e8 `* S- Z
- r.Use(authMiddleware(token))* v. W# y/ G) |1 T, Q) ^1 M
- r.GET("/", showInitDataMiddleware)' o! ~6 K( m; v
! @! g7 R. n& _. D4 X$ V# ]- if err := r.Run(":3000"); err != nil {7 |7 y* A8 }) F% V" e! y( U
- panic(err)5 B5 g7 ~8 B# T2 W: ?
- }1 K! C( ?' H' ? i& U
- }
复制代码 - V6 n# t+ {7 J, j" f9 f; S
7 {6 t* N1 m; s" A9 F# l
|