本帖最后由 riyad 于 2025-2-21 19:27 编辑
, e2 E! |/ ^- S/ t8 [2 J5 t! q& P
本文提供了不同编程语言的代码示例,说明开发人员如何使用 Telegram 初始数据授权用户。
3 k# b5 x1 H3 L: T' P' p, t客户端首先,需要从客户端向服务器传输初始数据开始。 我们可以使用此代码 : - import { retrieveLaunchParams } from '@telegram-apps/sdk';
( i+ l( w. b$ I) C - ( u4 U0 `4 r) D& j$ F0 H% S7 U
- const { initDataRaw } = retrieveLaunchParams();7 d/ H- C7 p5 m+ _; k1 ?9 L5 M8 ^
- 7 T+ {4 i6 o& C( \* f) r' z) a
- fetch('https://example.com/api', {
H7 D U# T- t" O1 y1 B - method: 'POST',. A5 Q4 h5 _$ |/ F
- headers: {9 H, j- B2 r0 F
- Authorization: `tma ${initDataRaw}`
' y4 T1 e0 x6 Y( B+ _; \ - },
; D5 E' I! I% f* ` - });
复制代码我们使用 https://example.com/api URL 向假想的服务器发送请求。 该请求使用 HTTP POST 方法(你可以使用任何你想要的方法),并附加 Authorization(授权)标头,这在这里是最重要的。 表示一个字符串,包含由空格分割的两个部分。 第一个描述授权方法(在 的情况下,我们的服务器将支持其他几种方法),第二个包含授权数据。 就 Telegram Mini Apps 而言,第二部分是原始 init 数据。
}- x3 x8 j( F4 M$ k4 K2 f2 Q* o服务器端现在,当 init 数据传输到服务器端时,我们应该创建一个简单的 HTTP 服务器,使用这些数据并 授权用户。 Node.jsNode.js 示例使用 express 处理 HTTP 请求。 - import { validate, parse, type InitDataParsed } from '@telegram-apps/init-data-node';
1 w @; h5 P3 W; W# f8 F - import express, {
. _1 H+ ]; A/ ?3 K, k3 ^- N - type ErrorRequestHandler,. |& A7 W5 o6 z5 }* u
- type RequestHandler,
' I! W! B# L$ w$ r& _: N+ O - type Response," ]. Z; o3 ?, F2 J3 W7 ~
- } from 'express';/ _9 n8 C# _4 j' S, _) j* @
- 1 l, @" i2 Z3 M
- /**
: ^( G# p: x$ h& i, A' Z" e: c1 p - * Sets init data in the specified Response object.
3 E9 m) \& N" ?! f( a( e6 @ - * @param res - Response object.6 ?2 E D o' |- W$ Z/ ?
- * @param initData - init data.
) }6 M8 D5 T+ h2 c- P" C: O- L( h - */
8 D; H* \5 x7 z4 I+ U1 i6 L- u - function setInitData(res: Response, initData: InitDataParsed): void {2 n% E7 E" s/ ]3 N: S8 O+ u
- res.locals.initData = initData;' Z. d1 \. \1 B" m& m2 s
- }6 |( Q4 K0 E D: O
- / z: z/ W8 O+ ]' T$ h ]
- /**
6 e4 ~# Z% N; H3 j7 s - * Extracts init data from the Response object.7 ^2 J6 W& X8 m
- * @param res - Response object.
. X1 O0 p) j; e, [. ?% j% u8 [ - * @returns Init data stored in the Response object. Can return undefined in case,4 W9 Q" G- O- C6 l2 ~
- * the client is not authorized.3 K* h+ m' q8 d
- */
& d) Y+ A9 v3 T+ |, n6 ~ - function getInitData(res: Response): InitDataParsed | undefined {1 _; {! |% `) u# Y, m, K0 d! _0 E H0 G
- return res.locals.initData;
9 @2 A1 X& a! s# B- b/ C* D - }
2 u+ V8 Y# J: B - : {: K& s& `: b2 G
- /**
3 h* y! C" z9 h; O - * Middleware which authorizes the external client.# i1 a4 w9 m$ \& S K! o7 j
- * @param req - Request object.
$ z8 |3 ?' P r - * @param res - Response object.
# `2 m- C9 z( j# ^4 @; U - * @param next - function to call the next middleware.0 f. Z$ @" z$ _8 W& y/ S/ c# S
- */
1 {, c% U: p) ~1 h# ~ - const authMiddleware: RequestHandler = (req, res, next) => {
. @& }4 E, K3 K* f$ Y - // We expect passing init data in the Authorization header in the following format:0 {& [ Z1 b5 X5 ^" b6 f
- // <auth-type> <auth-data>1 { o2 O% a' T& f- _ F
- // <auth-type> must be "tma", and <auth-data> is Telegram Mini Apps init data.+ ]0 T5 G2 W1 n' Z
- const [authType, authData = ''] = (req.header('authorization') || '').split(' ');% U/ ?$ k0 K& L u
. U' t' J4 y0 K8 F- switch (authType) {
- X2 S) _. s, [/ r/ r - case 'tma':; q' ^+ ]1 C, W" U3 t
- try {
4 s- O. j' P9 J6 ~2 P - // Validate init data.8 g- Z# a. B; D/ V( Z
- validate(authData, token, {. p ~- X# r: q" C5 F
- // We consider init data sign valid for 1 hour from their creation moment.
; Q3 {: q( Q) p+ { - expiresIn: 3600,# B4 N0 R: j: Q4 D/ ~1 e! T- p$ v
- });% T8 j# ~; b+ {0 f/ k; p. e) ?
. t4 l& n3 V0 y" U+ h W1 @- // Parse init data. We will surely need it in the future.
: o( j. \# Y* [' V( L+ ] - setInitData(res, parse(authData));
# _1 ~; \/ I* n1 p - return next();
6 I+ k+ ^# O. I, N; N* ]! b. @0 Y& X - } catch (e) {7 E1 D, ^( }* `
- return next(e);
+ y) l( r: z% b - }
# D4 Y9 {0 v& P8 _! K5 L - // ... other authorization methods.
: V5 G5 B7 ]- q' X - default:
1 l& g# n9 D0 k; J( E- Y! B8 T - return next(new Error('Unauthorized'));
! y1 p1 y+ `& j9 q5 g" C# G# Q - }! N( e6 w, p8 U f- f
- };5 {; Q( U6 w. V
% `$ K$ x) _' H& m# A) D- /**: [/ T- U$ W. T
- * Middleware which shows the user init data.# x# t2 B, A. f) z% K9 _* O8 d
- * @param _req
: R0 r3 U6 ~, l" a1 _ - * @param res - Response object.
4 T# t) J0 W. _2 m4 }, ` - * @param next - function to call the next middleware.
/ F2 e1 v5 K; o( j1 b) \ - */
. Y& G' ^& [' x1 R: x( o& } - const showInitDataMiddleware: RequestHandler = (_req, res, next) => {( _3 o0 E( Q1 E* D) `
- const initData = getInitData(res);: o+ p0 _- m( O* |0 G: }; U0 J
- if (!initData) {! l& o1 o. X9 Z' U% _1 E" k3 R) S( T3 K* L
- return next(new Error('Cant display init data as long as it was not found'));( `, f0 I5 z4 l5 S+ v5 f2 C
- }
: f2 ^! @! c6 [3 g( J - res.json(initData);8 ^6 P! M9 ~4 k0 {' b ~
- };
- d/ Y5 ]" e* g; H2 `* r
$ c" w/ l( A' j. u$ a8 j- /**
7 w# G4 O/ L) }- l1 T6 ~! ^! Y - * Middleware which displays the user init data. ^" k: ] U L. A& l. G: A
- * @param err - handled error.. K' R8 ]$ e% {5 c
- * @param _req6 V' V. h: Z) _2 |" c2 t5 i2 h
- * @param res - Response object.
8 R. ~- g. l) c. E7 L, X3 I$ p8 o - */
& _0 \ X2 o+ S$ V; U - const defaultErrorMiddleware: ErrorRequestHandler = (err, _req, res) => {
$ Q0 R5 v0 d2 V4 q3 U - res.status(500).json({# e* G3 p, U1 ]0 j d4 n
- error: err.message,; R2 {- a1 o% ?" O* S! J0 e: d/ O
- });7 x4 R$ K2 n0 Q8 w
- };; n0 e% K* m8 ~* Y/ W& _1 I
- * J/ Y0 u* c. U7 x1 S; P" R
- // Your secret bot token./ @, [' ?" m3 }5 i1 b$ m
- const token = '1234567890:ABC';
, e3 X9 ? g& Y! M) L
, B S) [, B0 M, K- // Create an Express applet and start listening to port 3000.& O# @& I& @7 d& A2 }
- const app = express();
& ^8 ]3 s9 ]& S0 R
& g+ V8 t9 b6 f% D7 v- t9 G0 j8 p- app.use(authMiddleware);( [- l; R/ {7 {/ M9 t* c
- app.get('/', showInitDataMiddleware);5 Q; M( ]+ t6 x8 o }) j7 C% G
- app.use(defaultErrorMiddleware);! M6 q* N, h7 z P8 _/ {3 N7 q) c
/ M6 d( L) O0 v" Q9 ]3 E- app.listen(3000);6 u# e8 T5 J4 }1 G! P9 m
- }+ [9 i8 F1 I2 X3 ~' k8 {- // After the HTTP server was launched, try sending an HTTP GET request to the URL . N. B, A9 x- Y0 o; s! m
- // http://localhost:3000/ with an Authorization header containing data in the required format.
复制代码
A+ R) s8 I$ ~4 n- K- BGoLang[color=var(--vp-c-brand-1)]GoLang 示例使用 [color=var(--vp-c-brand-1)]gin 处理 HTTP 请求。 - package main
7 B. u, G/ m& z) s4 H. u8 g - * U3 O; d$ N: f" B( B3 ~
- import (* m+ f4 c, ^# ?5 s3 M
- "context"0 r& o- x# z( Y, O6 Y% ?& c4 n
- "strings"& h0 ^5 D' f s
- "time"4 I2 O2 h" J' H6 R& d5 E8 d3 b, ?. W
- $ M6 ^8 @+ Y! n5 O
- "github.com/gin-gonic/gin"
' o; D ?% k5 @# w; J$ S- P, c - initdata "github.com/telegram-mini-apps/init-data-golang"
( e0 ?' K: O1 U h - )
; |* G1 C8 {0 J3 a O1 M4 ?) t
& S* ~7 m% x u1 P8 A- type contextKey string
H' y9 N) A8 n
5 v* }- S$ O% d9 N( j$ i; A* O7 P- const () ]9 v, O; Y+ A) T( f
- _initDataKey contextKey = "init-data"
0 `) }, X' `4 S - )5 Q: ~; C0 D, Y! D1 x
1 N6 o% o! c+ i, j9 g- // Returns new context with specified init data.
) c* `; V4 r8 h2 K - func withInitData(ctx context.Context, initData initdata.InitData) context.Context {
# D$ Q4 c* \, D5 |/ H - return context.WithValue(ctx, _initDataKey, initData)
$ Z# Z9 Q3 {# P$ m2 `* l6 L0 O0 f c - }
; M) l5 r* x# h& q" \ - . i* m3 |7 A# v7 }; C5 Y
- // Returns the init data from the specified context.% A* }6 Q. b0 D% l
- func ctxInitData(ctx context.Context) (initdata.InitData, bool) {
. J: s1 E3 A2 W2 h* _# o - initData, ok := ctx.Value(_initDataKey).(initdata.InitData)! m4 ?8 Z$ t- q! f& n* k+ K. A
- return initData, ok
* u- q. U! @ g9 }. v1 D* k/ W4 B - }
/ B5 R6 Y0 `' q# X9 N - 3 T" L& _) i6 x" X
- // Middleware which authorizes the external client." r- E* @+ V" U1 n0 l
- func authMiddleware(token string) gin.HandlerFunc {
7 }( `7 `6 l5 r$ \ - return func(context *gin.Context) {$ r6 y3 n- U; I8 j! c
- // We expect passing init data in the Authorization header in the following format:
! }" e& L4 R: x9 Y. K$ f; J6 U+ o - // <auth-type> <auth-data>
! b; j5 R3 F& W2 p$ a - // <auth-type> must be "tma", and <auth-data> is Telegram Mini Apps init data.
, l$ m+ |; p8 A9 K* Z - authParts := strings.Split(context.GetHeader("authorization"), " ")( Y: K& x5 E. M3 D$ R
- if len(authParts) != 2 {
' G- D$ p' z! W' T" y' @ - context.AbortWithStatusJSON(401, map[string]any{
$ E/ b/ N' A; `2 @' ] - "message": "Unauthorized",
~" C# \$ l. Z' A8 u/ }- ~) n3 s - })
) M, p# Y0 e0 ]) I& B1 @9 L - return/ s% Q$ x0 t+ t) S1 ]2 _
- }
2 t5 Y% A7 z# A/ d h. J; @' K
* X3 d! m; W/ e- authType := authParts[0]7 j3 _5 A1 ?- w
- authData := authParts[1]# p2 D. l8 q$ S r& o7 A
; S6 n% |9 x# ?& h( X) u- switch authType {7 f% n% k h, |; u
- case "tma":
& V+ a& ^! q$ V. [4 E5 ^ - // Validate init data. We consider init data sign valid for 1 hour from their
4 v" y- t& f. L$ [ - // creation moment." G; {! W4 x: C0 h
- if err := initdata.Validate(authData, token, time.Hour); err != nil {
& @, S0 [8 ~5 T, D - context.AbortWithStatusJSON(401, map[string]any{! O1 p1 X4 v& Q: v, f, K* |
- "message": err.Error(),+ U i( P8 n8 c$ G
- })4 O9 ^/ f8 z: {7 D
- return
0 e" n) V$ B" r. \; j% h/ n9 H2 Q - }2 |! ]6 ^8 k# W# X9 ]- k9 J
# C/ J' A& p) W" z+ {2 E% W |- // Parse init data. We will surely need it in the future.
( R" T0 o$ M# O0 T y& \ - initData, err := initdata.Parse(authData)7 [2 x4 A" P1 W# v9 M
- if err != nil {
! S- N x1 u" ? - context.AbortWithStatusJSON(500, map[string]any{6 j7 ^9 U1 P! r' z: _- z; v
- "message": err.Error(),
$ u( w, y: n( l2 r4 N6 w, ^2 \9 Q0 s8 B T - })3 y7 F7 ~+ K: M D, Q, Y- w) E
- return! F$ K6 {3 r$ ?1 Z
- }
/ ~" a3 `7 u. F l% ~% b - 0 Y1 F' x. e' _9 M; p) H5 S
- context.Request = context.Request.WithContext(; Q m$ |2 G! R, u
- withInitData(context.Request.Context(), initData),- D4 i4 m' C7 T) _5 O) o7 k
- )5 s8 s1 q. R6 r$ i3 V A' X, {
- }
6 _6 z* \% m6 y: C# Q* C - }: y( B" ]: E" |1 u8 t+ j" M
- }7 J3 A2 @$ i) v7 H3 f$ Y) R6 T
) z! P1 x' ]+ ~7 e- // Middleware which shows the user init data.- M/ {9 c- L3 G' C: N6 B# e. L. o
- func showInitDataMiddleware(context *gin.Context) {) d, W2 p5 e# }4 X
- initData, ok := ctxInitData(context.Request.Context())' s9 ]+ n! A3 i4 [% V
- if !ok {' w/ h$ y7 X6 k0 R
- context.AbortWithStatusJSON(401, map[string]any{
" A: ~: J3 m' i8 W; ] - "message": "Init data not found",, d2 d* g: c# M
- })
4 x2 o% A+ |! m( W - return
1 d8 {* c( N$ I - }
- J! J9 V% ~$ M5 L) l
' {& e! n2 T n* {- context.JSON(200, initData)$ T/ X h0 C9 N
- }' J# Q! C% J$ M, b+ |
- H' X) M3 H- R" p: H- func main() {
- R5 ~" {, W- ^* b: R0 U - // Your secret bot token.5 b: Z$ F: R# U2 s+ h5 H; E
- token := "1234567890:ABC"
& ^6 ?" ^6 q# ^' I! b8 H2 s9 c, A3 z
# } x0 A4 x+ B$ U3 d) w- r := gin.New()
: a. _9 [4 H& }" l5 o$ ?
$ c7 r8 O) U5 T5 C- r.Use(authMiddleware(token))
/ a6 @! T0 ]$ g5 T9 E# L: P1 F - r.GET("/", showInitDataMiddleware)1 S: }# `5 x7 J& O7 ?4 g3 W
- $ @& x1 }# C$ Y. A+ c
- if err := r.Run(":3000"); err != nil {
' D( o) T6 A4 p- i- l: j8 ]3 l - panic(err)0 A4 b/ y* l; I* G9 w
- }# H6 y8 v0 e5 G: P( R3 ?
- }
复制代码 7 t7 S6 r- k) H+ S
7 k; Y; s! B8 |8 r( k
|