本帖最后由 riyad 于 2025-2-21 19:27 编辑
/ E: m' \& K/ l- I# \1 k7 C5 I: Y0 s" g1 _6 M4 Q( F. i* ]
本文提供了不同编程语言的代码示例,说明开发人员如何使用 Telegram 初始数据授权用户。 : X. @8 F+ J0 x1 g1 d. u
客户端首先,需要从客户端向服务器传输初始数据开始。 我们可以使用此代码 : - import { retrieveLaunchParams } from '@telegram-apps/sdk';
! V: c: R, U. \- C9 V0 g! k - ! k, C& r$ L. Z; p3 r' z0 C
- const { initDataRaw } = retrieveLaunchParams();! O' m: ]3 u( L, H8 t
# f9 r5 {) Z) B, P3 N. S- fetch('https://example.com/api', {% F$ ?* }# e7 w: n
- method: 'POST',
. T- A' u) ?3 @$ M+ J1 m - headers: {9 l/ S* ~9 ~ t7 E- k0 e( F# [
- Authorization: `tma ${initDataRaw}`( r) R3 L+ q( {% O: h" l$ X
- },# J8 A2 E$ y0 r- y W0 c
- });
复制代码我们使用 https://example.com/api URL 向假想的服务器发送请求。 该请求使用 HTTP POST 方法(你可以使用任何你想要的方法),并附加 Authorization(授权)标头,这在这里是最重要的。 表示一个字符串,包含由空格分割的两个部分。 第一个描述授权方法(在 的情况下,我们的服务器将支持其他几种方法),第二个包含授权数据。 就 Telegram Mini Apps 而言,第二部分是原始 init 数据。
' f3 m% c2 f, R# U服务器端现在,当 init 数据传输到服务器端时,我们应该创建一个简单的 HTTP 服务器,使用这些数据并 授权用户。 Node.jsNode.js 示例使用 express 处理 HTTP 请求。 - import { validate, parse, type InitDataParsed } from '@telegram-apps/init-data-node'; a/ Q1 y$ J9 G7 `/ k( e v
- import express, {3 @1 `' I+ X! N+ u+ Z8 A+ K# P
- type ErrorRequestHandler,& G" n# U( L; j8 U& R
- type RequestHandler,6 i: Z% o+ U% e5 w- Q+ _, Z
- type Response,% k/ b, h, }* T# K
- } from 'express';, k3 c1 f1 z1 [
- 9 v* s* E' f5 X1 ~0 f
- /**
. b: x* s: C5 f - * Sets init data in the specified Response object.3 L% t' s. e, I r) R5 ]3 f9 R- E
- * @param res - Response object.
# \/ D0 x, @; O) g0 ` - * @param initData - init data.% \, `' `* x4 U0 T E5 o
- */
2 Z- o3 a7 j# b! l - function setInitData(res: Response, initData: InitDataParsed): void {9 e0 ]! z7 y+ j2 u
- res.locals.initData = initData;
5 K) E( e7 q- F2 C" _ - }
% x4 T9 V, F6 i& E - ; q5 F) ]/ r6 S3 L/ x, b: ]
- /**/ `5 J; v) u0 D! ^0 @
- * Extracts init data from the Response object.6 j- g" n, V! T6 f% }8 N
- * @param res - Response object.% O0 B t, K6 a6 W
- * @returns Init data stored in the Response object. Can return undefined in case,, a/ w! l3 y( R
- * the client is not authorized.
: ]) h6 B2 x1 S - */2 Z7 N9 o+ K0 E
- function getInitData(res: Response): InitDataParsed | undefined {
3 ]# M( ]! w! R' y - return res.locals.initData;
$ K$ f: O3 x7 V$ Q2 I - }
! r! D0 F: w2 [ p5 S# x
; c' y9 F- r( V, @2 D' x" s4 N- /**) }0 K. V) h$ I. q2 ^1 q8 x/ q
- * Middleware which authorizes the external client.
0 ~9 W2 J' B1 o/ J - * @param req - Request object.
7 A: t3 [8 G+ t9 y" E. n' W! ]" B - * @param res - Response object.. n; ` @# D9 U
- * @param next - function to call the next middleware.) H/ v* [6 @3 \1 S& r# V
- */( U2 y |6 x5 \# y* y+ j% n) j
- const authMiddleware: RequestHandler = (req, res, next) => {% g0 X W. S/ P3 V+ `3 \) h
- // We expect passing init data in the Authorization header in the following format:' `! M+ G* j- v% u. ]9 S
- // <auth-type> <auth-data>6 G7 U1 E8 F& D# k8 S# J- h; j
- // <auth-type> must be "tma", and <auth-data> is Telegram Mini Apps init data.
) B) F/ m8 J+ } Y' i* _9 S" o - const [authType, authData = ''] = (req.header('authorization') || '').split(' ');
3 Z( w4 q2 J$ P/ {8 g( \. M! ~ - . O9 c. [. g1 Y. O1 O
- switch (authType) {9 U3 q7 [% J7 l3 \$ P$ T
- case 'tma':
: Y$ z# I, z) U! p, a! ] - try {7 O! F* h- l' E$ }' Y
- // Validate init data.
) e0 F. K5 j# [9 I' T |+ p& f - validate(authData, token, {6 t6 V# c" N/ y7 ~& p
- // We consider init data sign valid for 1 hour from their creation moment./ O7 C0 K. m6 ^* y9 X( \" K
- expiresIn: 3600,
' d( }4 r* B, `: y+ i - });3 I8 f1 Z0 D9 [
+ @/ z9 n6 B' ~2 m4 q. _4 |0 G1 S- // Parse init data. We will surely need it in the future.
) h, x1 ^8 k: A( ?" t - setInitData(res, parse(authData));; A8 c" R! S* \- ], R9 U. Z9 R3 W. B
- return next();9 G5 x& \" w- b% n- M8 U
- } catch (e) {
+ P3 y7 Z+ h4 h - return next(e);9 y2 Y( y% r9 j3 ^/ P( W
- }$ J7 c& D2 L7 E! |, V
- // ... other authorization methods.* _# p4 e- x* y. k; Q+ f0 C+ q
- default:
2 v6 O# H- Z) l7 n) p, W6 M, H' V - return next(new Error('Unauthorized'));
! V7 w- K0 |0 B+ C& h7 l - }
K1 l, Y6 S$ U - };; T0 l6 A" ~' N, _0 n
- ( o0 A4 F9 l5 \) K
- /**7 r' C3 q7 v" }5 p
- * Middleware which shows the user init data.' R* p) c8 n9 K: `* |. Q \4 Y
- * @param _req# ?( @! U5 v6 U6 \! X8 s: y: C+ V
- * @param res - Response object.: W( R6 |! s: r/ i# q. e, H
- * @param next - function to call the next middleware.1 \6 n* X( g; a
- */
) f6 }% c# G* @2 k- |& Y& `% V - const showInitDataMiddleware: RequestHandler = (_req, res, next) => {
2 M) H; f/ e ~. t( x - const initData = getInitData(res);
! z- t& z* k8 Y5 X& o - if (!initData) {
" k' v. ]2 Q+ ~ - return next(new Error('Cant display init data as long as it was not found'));2 s3 ~9 ]' ]8 c" ?
- }
* Q) a. K3 j1 N: ]9 J1 b3 ^ - res.json(initData);
9 w; o8 H$ X' j - };
2 ~/ g+ J8 l$ U0 ~0 e/ i - # K: H; m! i [9 m" z; v% q C
- /**. W$ l+ U1 q& b, v3 _/ ^; M
- * Middleware which displays the user init data.
' C# C$ J6 |9 Y0 s) C: J - * @param err - handled error.* a8 _1 E7 P2 V# Z) q, p- [
- * @param _req9 X) k. \9 a* g% k4 N
- * @param res - Response object.9 o% ~2 S: y/ \* K) j% R
- */
+ _# g; y# m0 a( c3 y - const defaultErrorMiddleware: ErrorRequestHandler = (err, _req, res) => {
5 ~1 `: \: y5 F$ {: g/ T/ b - res.status(500).json({5 G! U* x0 ]0 ]/ v) [9 m: m% s9 Y
- error: err.message, ~+ v6 c5 [/ k6 z8 ]! P( \# f
- });' ]; M; Z' t& O' i' r8 `! z) g
- };. ~* g& d" E0 R' a, x* W' E
& V& R' q. W5 h- // Your secret bot token.
4 I; K% s. p: P" C - const token = '1234567890:ABC';
; `8 C/ E. G _6 T) e - ) B$ j8 x- _9 z, _# }
- // Create an Express applet and start listening to port 3000.+ \4 |) V) e. v+ a8 r9 Y
- const app = express();
- Y- Z; e4 O; @: t6 g
& M( L8 g/ w2 J2 v- N" r- app.use(authMiddleware);
; \7 f2 ?4 V" U - app.get('/', showInitDataMiddleware);
% e" Z8 o+ C$ h$ }) P/ a+ V+ m - app.use(defaultErrorMiddleware);
5 m4 _) q2 s3 [: ^: B - 1 ~, y8 o; a& O% K7 k( P A) N
- app.listen(3000);' L" J9 B7 X! o- r
' ~7 v7 ~# M5 e, ^- // After the HTTP server was launched, try sending an HTTP GET request to the URL
/ ]1 ]3 C$ ~: E( i. @0 p - // http://localhost:3000/ with an Authorization header containing data in the required format.
复制代码
6 M) k4 @' }0 r9 Q r, ^GoLang[color=var(--vp-c-brand-1)]GoLang 示例使用 [color=var(--vp-c-brand-1)]gin 处理 HTTP 请求。 - package main
$ e$ X6 S4 U! I7 ?
! W& \4 G2 a, X4 N- import (0 E/ d6 Q( ~) U
- "context"! n8 z/ \, F; B) f/ x4 w
- "strings"
; A- e S) b$ F, I" d T! K7 C - "time"
% o- t8 E( P) J& T- k3 h5 g: [9 g
) J& [. U9 G3 h$ u/ p& b/ D- "github.com/gin-gonic/gin", R- m7 ^- h( t- h* q8 O& F$ V* i7 H
- initdata "github.com/telegram-mini-apps/init-data-golang" e6 J+ l% w4 o3 ?/ `
- )
4 Y, d* X' U# @) {4 [" O; t6 F! D
/ `7 k. Y3 E' H) P; o0 F5 b- type contextKey string4 p6 G8 }" R& I8 @8 p) O
' W7 ~" T) S0 h- q. x- const (
0 t; S" c$ d) s/ m } - _initDataKey contextKey = "init-data"% d) ?9 D5 d& g1 ]; ^! A4 Y" g
- )1 B2 x- |% w2 P, K# Z
- & B4 o/ r- w9 \* |" Z5 ]1 t
- // Returns new context with specified init data.
4 i/ u* y- P' p" u* t - func withInitData(ctx context.Context, initData initdata.InitData) context.Context { I" `2 \6 |4 I r% p
- return context.WithValue(ctx, _initDataKey, initData)) C, ^! l# P& p# v5 O0 U6 N
- }: P4 e7 b. ~0 q) v/ [- m) w& Q3 ?
- ! b" f! D/ h- w+ F; r" m( H# n
- // Returns the init data from the specified context.
: U! L# r6 n+ ]/ e( o9 G4 _% P2 \ - func ctxInitData(ctx context.Context) (initdata.InitData, bool) {
0 v" \6 b' M) t* o1 a3 p; a - initData, ok := ctx.Value(_initDataKey).(initdata.InitData)
* k$ z, ?8 ^4 U/ r - return initData, ok/ k3 u! b/ x# e3 ^5 Y+ c7 q$ A
- }6 s c0 J9 ]0 H% ~
- 0 ~+ t+ W! J# B1 O) j& H/ a( Q
- // Middleware which authorizes the external client.( P' u, `) ~3 ^; u7 C- d" W1 s* V: [
- func authMiddleware(token string) gin.HandlerFunc {- o; w6 d* P9 @3 d+ P( u
- return func(context *gin.Context) {
0 J8 ^8 D- i; u \) [3 r& d - // We expect passing init data in the Authorization header in the following format:
0 |) G6 U7 P; R j" n5 @7 ^ - // <auth-type> <auth-data>
. `' M6 H2 X3 b1 V0 M$ [ - // <auth-type> must be "tma", and <auth-data> is Telegram Mini Apps init data./ m H, @$ R" r' ^( Z
- authParts := strings.Split(context.GetHeader("authorization"), " ")5 T1 d3 n, l, y4 c
- if len(authParts) != 2 {8 I+ D* N9 F% G( {& |6 l3 v
- context.AbortWithStatusJSON(401, map[string]any{
: D$ |* D- n* x5 J) g4 y& {+ k - "message": "Unauthorized",8 R/ h/ c! Y1 x- _' B4 P7 e8 y$ m
- })
8 _6 Q4 I# H4 _# k6 F" U' |4 N3 X - return6 j' F+ P* p% u
- }
: C0 w) _3 c. U7 i
4 E( |3 {) S) L9 w5 a- authType := authParts[0]
$ ~7 K# T6 R E7 t( f( D - authData := authParts[1]
- Z2 R- t: Q3 i6 S. w
5 ^4 N7 z: j1 y- s- switch authType {
; N3 h/ ?( ]8 P7 x - case "tma":
5 X2 o) }+ R) `0 P( ~2 M6 ?0 p6 v - // Validate init data. We consider init data sign valid for 1 hour from their
* E! i, `3 I# Y+ C! @ - // creation moment.
) @: ]- S1 ]' g7 x - if err := initdata.Validate(authData, token, time.Hour); err != nil {4 t5 r+ R5 s6 o5 T! A! I& s
- context.AbortWithStatusJSON(401, map[string]any{: V) h: q( f i4 S
- "message": err.Error(),: `% ?$ p3 ^$ U+ S6 [% t
- })* a) f. `! y8 {; B
- return
, U* o+ N3 K3 P/ X - }% g5 {- r# a) R! u0 F
7 O$ g+ P0 E. N5 |; r* L, J- // Parse init data. We will surely need it in the future.* k# F0 D% x$ D% C' W/ P+ J6 J
- initData, err := initdata.Parse(authData)# Q* N1 K9 _3 e8 j! `, b% S
- if err != nil {( @, r8 r! F1 i$ z- U. Z4 W
- context.AbortWithStatusJSON(500, map[string]any{' n1 t* g, f- D2 B) X6 o
- "message": err.Error(),
" C i8 m: {% h$ \: @ - })
" s4 E' W H; L; c" Z3 w - return
8 c8 m# E. f4 \4 s- e- _! `8 J1 E - }
5 Y8 ~( |9 Z7 t2 c) L# q; V - 4 d- W' ?1 K, ^
- context.Request = context.Request.WithContext(6 d6 P" O5 H* X( S5 D
- withInitData(context.Request.Context(), initData),/ g. `) H2 g# \
- )# {/ Q" x( J8 c) \: [
- }4 w5 N7 x6 {* ^8 q. K6 y0 ~/ G
- }
' B9 Z" z3 F7 i+ N4 E7 o7 l - }9 `9 b* Q- I6 N. h
- & H: \2 L: m1 O2 }1 ~, P
- // Middleware which shows the user init data.3 k1 ]4 D5 n$ N0 F. u |* D( {
- func showInitDataMiddleware(context *gin.Context) {/ H0 H B0 B7 S. R4 I6 L! M$ o' Q2 e3 h
- initData, ok := ctxInitData(context.Request.Context())
( | `1 l+ m) { - if !ok {
5 O% G# q1 a( `0 W# Q5 q4 h - context.AbortWithStatusJSON(401, map[string]any{
$ M* C. c) r7 K7 w) Q8 W - "message": "Init data not found",
9 k: `6 ]; H4 ]$ O - })
, e- P. C& W. ^: R+ L& e3 B* j - return! l0 H/ k" D z
- }
7 U* q' T+ ]0 }: a; M/ C+ B - 8 J) u; w4 @5 W4 e1 J$ W
- context.JSON(200, initData)
" |/ [8 ^! ~- R6 Y' {% R, b5 J - }1 A! ~0 {+ B; C+ T. W. V) s4 n
- ; r( f; R5 @$ p
- func main() {6 u( D$ ^2 F; W) a. g: I
- // Your secret bot token.
% O! D8 u& a5 Z - token := "1234567890:ABC"
' d" X' ]! s; n U7 a0 k
/ P1 o6 T3 B- T& b+ U" B' J- r := gin.New()$ Z' l8 z. G" b/ t( \" X; J3 l
$ j8 E* _6 n6 }* p3 P6 b- r.Use(authMiddleware(token))6 J. Q( n. `9 j! G! X
- r.GET("/", showInitDataMiddleware)5 b3 B8 I1 [- C
- 2 L2 W# T& N5 C0 e$ f0 A5 |0 k4 l
- if err := r.Run(":3000"); err != nil {% V! ] F: f. p- z
- panic(err)
4 [& @/ U+ k' S" C( |5 h - }
, m% A6 m& v# i& L$ z9 o! g - }
复制代码 3 L, a# L8 X3 S
2 }9 A" [1 F1 k1 B d$ j- A0 G9 r |