授权用户
本文提供了不同编程语言的代码示例,说明开发人员如何使用 Telegram 初始数据授权用户。
客户端
首先,需要从客户端向服务器传输初始数据开始。 我们可以使用此代码 :
typescript
import { retrieveLaunchParams } from '@telegram-apps/sdk';
const { initDataRaw } = retrieveLaunchParams();
fetch('https://example.com/api', {
method: 'POST',
headers: {
Authorization: `tma ${initDataRaw}`
},
});
我们使用 https://example.com/api
URL 向假想的服务器发送请求。 该请求使用 HTTP POST 方法(你可以使用任何你想要的方法),并附加 Authorization
(授权)标头,这在这里是最重要的。 表示一个字符串,包含由空格分割的两个部分。 第一个描述授权方法(在 的情况下,我们的服务器将支持其他几种方法),第二个包含授权数据。 就 Telegram Mini Apps 而言,第二部分是原始 init 数据。
服务器端
现在,当 init 数据传输到服务器端时,我们应该创建一个简单的 HTTP 服务器,使用这些数据并 授权用户。
Node.js
Node.js 示例使用 express 处理 HTTP 请求。
typescript
import { validate, parse, type InitDataParsed } from '@telegram-apps/init-data-node';
import express, {
type ErrorRequestHandler,
type RequestHandler,
type Response,
} from 'express';
/**
* Sets init data in the specified Response object.
* @param res - Response object.
* @param initData - init data.
*/
function setInitData(res: Response, initData: InitDataParsed): void {
res.locals.initData = initData;
}
/**
* Extracts init data from the Response object.
* @param res - Response object.
* @returns Init data stored in the Response object. Can return undefined in case,
* the client is not authorized.
*/
function getInitData(res: Response): InitDataParsed | undefined {
return res.locals.initData;
}
/**
* Middleware which authorizes the external client.
* @param req - Request object.
* @param res - Response object.
* @param next - function to call the next middleware.
*/
const authMiddleware: RequestHandler = (req, res, next) => {
// We expect passing init data in the Authorization header in the following format:
// <auth-type> <auth-data>
// <auth-type> must be "tma", and <auth-data> is Telegram Mini Apps init data.
const [authType, authData = ''] = (req.header('authorization') || '').split(' ');
switch (authType) {
case 'tma':
try {
// Validate init data.
validate(authData, token, {
// We consider init data sign valid for 1 hour from their creation moment.
expiresIn: 3600,
});
// Parse init data. We will surely need it in the future.
setInitData(res, parse(authData));
return next();
} catch (e) {
return next(e);
}
// ... other authorization methods.
default:
return next(new Error('Unauthorized'));
}
};
/**
* Middleware which shows the user init data.
* @param _req
* @param res - Response object.
* @param next - function to call the next middleware.
*/
const showInitDataMiddleware: RequestHandler = (_req, res, next) => {
const initData = getInitData(res);
if (!initData) {
return next(new Error('Cant display init data as long as it was not found'));
}
res.json(initData);
};
/**
* Middleware which displays the user init data.
* @param err - handled error.
* @param _req
* @param res - Response object.
*/
const defaultErrorMiddleware: ErrorRequestHandler = (err, _req, res) => {
res.status(500).json({
error: err.message,
});
};
// Your secret bot token.
const token = '1234567890:ABC';
// Create an Express applet and start listening to port 3000.
const app = express();
app.use(authMiddleware);
app.get('/', showInitDataMiddleware);
app.use(defaultErrorMiddleware);
app.listen(3000);
// After the HTTP server was launched, try sending an HTTP GET request to the URL
// http://localhost:3000/ with an Authorization header containing data in the required format.
GoLang
GoLang 示例使用 gin 处理 HTTP 请求。
go
package main
import (
"context"
"strings"
"time"
"github.com/gin-gonic/gin"
initdata "github.com/telegram-mini-apps/init-data-golang"
)
type contextKey string
const (
_initDataKey contextKey = "init-data"
)
// Returns new context with specified init data.
func withInitData(ctx context.Context, initData initdata.InitData) context.Context {
return context.WithValue(ctx, _initDataKey, initData)
}
// Returns the init data from the specified context.
func ctxInitData(ctx context.Context) (initdata.InitData, bool) {
initData, ok := ctx.Value(_initDataKey).(initdata.InitData)
return initData, ok
}
// Middleware which authorizes the external client.
func authMiddleware(token string) gin.HandlerFunc {
return func(context *gin.Context) {
// We expect passing init data in the Authorization header in the following format:
// <auth-type> <auth-data>
// <auth-type> must be "tma", and <auth-data> is Telegram Mini Apps init data.
authParts := strings.Split(context.GetHeader("authorization"), " ")
if len(authParts) != 2 {
context.AbortWithStatusJSON(401, map[string]any{
"message": "Unauthorized",
})
return
}
authType := authParts[0]
authData := authParts[1]
switch authType {
case "tma":
// Validate init data. We consider init data sign valid for 1 hour from their
// creation moment.
if err := initdata.Validate(authData, token, time.Hour); err != nil {
context.AbortWithStatusJSON(401, map[string]any{
"message": err.Error(),
})
return
}
// Parse init data. We will surely need it in the future.
initData, err := initdata.Parse(authData)
if err != nil {
context.AbortWithStatusJSON(500, map[string]any{
"message": err.Error(),
})
return
}
context.Request = context.Request.WithContext(
withInitData(context.Request.Context(), initData),
)
}
}
}
// Middleware which shows the user init data.
func showInitDataMiddleware(context *gin.Context) {
initData, ok := ctxInitData(context.Request.Context())
if !ok {
context.AbortWithStatusJSON(401, map[string]any{
"message": "Init data not found",
})
return
}
context.JSON(200, initData)
}
func main() {
// Your secret bot token.
token := "1234567890:ABC"
r := gin.New()
r.Use(authMiddleware(token))
r.GET("/", showInitDataMiddleware)
if err := r.Run(":3000"); err != nil {
panic(err)
}
}