Merge pull request #9750 from appwrite/update-tokens

Update tokens without JWT
This commit is contained in:
Eldad A. Fux 2025-05-15 14:19:41 +02:00 committed by GitHub
commit 23d73e1f3e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 2968 additions and 2976 deletions

View file

@ -499,6 +499,11 @@ return [
'description' => 'The requested file token has expired.',
'code' => 401,
],
Exception::TOKEN_RESOURCE_TYPE_INVALID => [
'name' => Exception::TOKEN_RESOURCE_TYPE_INVALID,
'description' => 'The resource type for the token is invalid.',
'code' => 400,
],
/** VCS */
Exception::INSTALLATION_NOT_FOUND => [

View file

@ -2646,7 +2646,10 @@
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/account\/create-token-email.md",
"rate-limit": 10,
"rate-time": 3600,
"rate-key": "url:{url},email:{param-email}",
"rate-key": [
"url:{url},email:{param-email}",
"url:{url},ip:{ip}"
],
"scope": "sessions.write",
"platforms": [
"server",
@ -4427,7 +4430,7 @@
"tags": [
"databases"
],
"description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n",
"description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.",
"responses": {
"201": {
"description": "Document",
@ -4459,6 +4462,51 @@
"server"
],
"packaging": false,
"methods": [
{
"name": "createDocument",
"parameters": [
"databaseId",
"collectionId",
"documentId",
"data",
"permissions"
],
"required": [
"databaseId",
"collectionId",
"documentId",
"data"
],
"responses": [
{
"code": 201,
"model": "#\/components\/schemas\/document"
}
],
"description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console."
},
{
"name": "createDocuments",
"parameters": [
"databaseId",
"collectionId",
"documents"
],
"required": [
"databaseId",
"collectionId",
"documents"
],
"responses": [
{
"code": 201,
"model": "#\/components\/schemas\/documentList"
}
],
"description": "Create new Documents. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console."
}
],
"auth": {
"Project": []
}
@ -4515,12 +4563,16 @@
"items": {
"type": "string"
}
},
"documents": {
"type": "array",
"description": "Array of documents data as JSON objects.",
"x-example": null,
"items": {
"type": "object"
}
}
},
"required": [
"documentId",
"data"
]
}
}
}
}
@ -4744,7 +4796,7 @@
"x-appwrite": {
"method": "deleteDocument",
"group": "documents",
"weight": 114,
"weight": 116,
"cookies": false,
"type": "",
"deprecated": false,
@ -4828,7 +4880,7 @@
"x-appwrite": {
"method": "listExecutions",
"group": "executions",
"weight": 388,
"weight": 391,
"cookies": false,
"type": "",
"deprecated": false,
@ -4903,7 +4955,7 @@
"x-appwrite": {
"method": "createExecution",
"group": "executions",
"weight": 386,
"weight": 389,
"cookies": false,
"type": "",
"deprecated": false,
@ -5018,7 +5070,7 @@
"x-appwrite": {
"method": "getExecution",
"group": "executions",
"weight": 387,
"weight": 390,
"cookies": false,
"type": "",
"deprecated": false,
@ -5092,7 +5144,7 @@
"x-appwrite": {
"method": "query",
"group": "graphql",
"weight": 302,
"weight": 305,
"cookies": false,
"type": "graphql",
"deprecated": false,
@ -5144,7 +5196,7 @@
"x-appwrite": {
"method": "mutation",
"group": "graphql",
"weight": 301,
"weight": 304,
"cookies": false,
"type": "graphql",
"deprecated": false,
@ -5196,7 +5248,7 @@
"x-appwrite": {
"method": "get",
"group": null,
"weight": 118,
"weight": 121,
"cookies": false,
"type": "",
"deprecated": false,
@ -5248,7 +5300,7 @@
"x-appwrite": {
"method": "listCodes",
"group": null,
"weight": 119,
"weight": 122,
"cookies": false,
"type": "",
"deprecated": false,
@ -5300,7 +5352,7 @@
"x-appwrite": {
"method": "listContinents",
"group": null,
"weight": 123,
"weight": 126,
"cookies": false,
"type": "",
"deprecated": false,
@ -5352,7 +5404,7 @@
"x-appwrite": {
"method": "listCountries",
"group": null,
"weight": 120,
"weight": 123,
"cookies": false,
"type": "",
"deprecated": false,
@ -5404,7 +5456,7 @@
"x-appwrite": {
"method": "listCountriesEU",
"group": null,
"weight": 121,
"weight": 124,
"cookies": false,
"type": "",
"deprecated": false,
@ -5456,7 +5508,7 @@
"x-appwrite": {
"method": "listCountriesPhones",
"group": null,
"weight": 122,
"weight": 125,
"cookies": false,
"type": "",
"deprecated": false,
@ -5508,7 +5560,7 @@
"x-appwrite": {
"method": "listCurrencies",
"group": null,
"weight": 124,
"weight": 127,
"cookies": false,
"type": "",
"deprecated": false,
@ -5560,7 +5612,7 @@
"x-appwrite": {
"method": "listLanguages",
"group": null,
"weight": 125,
"weight": 128,
"cookies": false,
"type": "",
"deprecated": false,
@ -5612,7 +5664,7 @@
"x-appwrite": {
"method": "createSubscriber",
"group": "subscribers",
"weight": 348,
"weight": 351,
"cookies": false,
"type": "",
"deprecated": false,
@ -5695,7 +5747,7 @@
"x-appwrite": {
"method": "deleteSubscriber",
"group": "subscribers",
"weight": 352,
"weight": 355,
"cookies": false,
"type": "",
"deprecated": false,
@ -5770,7 +5822,7 @@
"x-appwrite": {
"method": "listFiles",
"group": "files",
"weight": 208,
"weight": 211,
"cookies": false,
"type": "",
"deprecated": false,
@ -5856,7 +5908,7 @@
"x-appwrite": {
"method": "createFile",
"group": "files",
"weight": 207,
"weight": 210,
"cookies": false,
"type": "upload",
"deprecated": false,
@ -5954,7 +6006,7 @@
"x-appwrite": {
"method": "getFile",
"group": "files",
"weight": 209,
"weight": 212,
"cookies": false,
"type": "",
"deprecated": false,
@ -6026,7 +6078,7 @@
"x-appwrite": {
"method": "updateFile",
"group": "files",
"weight": 214,
"weight": 217,
"cookies": false,
"type": "",
"deprecated": false,
@ -6115,7 +6167,7 @@
"x-appwrite": {
"method": "deleteFile",
"group": "files",
"weight": 215,
"weight": 218,
"cookies": false,
"type": "",
"deprecated": false,
@ -6182,7 +6234,7 @@
"x-appwrite": {
"method": "getFileDownload",
"group": "files",
"weight": 211,
"weight": 214,
"cookies": false,
"type": "location",
"deprecated": false,
@ -6229,6 +6281,17 @@
"x-example": "<FILE_ID>"
},
"in": "path"
},
{
"name": "token",
"description": "File token for accessing this file.",
"required": false,
"schema": {
"type": "string",
"x-example": "<TOKEN>",
"default": ""
},
"in": "query"
}
]
}
@ -6249,7 +6312,7 @@
"x-appwrite": {
"method": "getFilePreview",
"group": "files",
"weight": 210,
"weight": 213,
"cookies": false,
"type": "location",
"deprecated": false,
@ -6446,6 +6509,17 @@
"default": ""
},
"in": "query"
},
{
"name": "token",
"description": "File token for accessing this file.",
"required": false,
"schema": {
"type": "string",
"x-example": "<TOKEN>",
"default": ""
},
"in": "query"
}
]
}
@ -6466,7 +6540,7 @@
"x-appwrite": {
"method": "getFileView",
"group": "files",
"weight": 212,
"weight": 215,
"cookies": false,
"type": "location",
"deprecated": false,
@ -6513,6 +6587,17 @@
"x-example": "<FILE_ID>"
},
"in": "path"
},
{
"name": "token",
"description": "File token for accessing this file.",
"required": false,
"schema": {
"type": "string",
"x-example": "<TOKEN>",
"default": ""
},
"in": "query"
}
]
}
@ -6540,7 +6625,7 @@
"x-appwrite": {
"method": "list",
"group": "teams",
"weight": 219,
"weight": 222,
"cookies": false,
"type": "",
"deprecated": false,
@ -6616,7 +6701,7 @@
"x-appwrite": {
"method": "create",
"group": "teams",
"weight": 218,
"weight": 221,
"cookies": false,
"type": "",
"deprecated": false,
@ -6701,7 +6786,7 @@
"x-appwrite": {
"method": "get",
"group": "teams",
"weight": 220,
"weight": 223,
"cookies": false,
"type": "",
"deprecated": false,
@ -6763,7 +6848,7 @@
"x-appwrite": {
"method": "updateName",
"group": "teams",
"weight": 222,
"weight": 225,
"cookies": false,
"type": "",
"deprecated": false,
@ -6837,7 +6922,7 @@
"x-appwrite": {
"method": "delete",
"group": "teams",
"weight": 224,
"weight": 227,
"cookies": false,
"type": "",
"deprecated": false,
@ -6901,7 +6986,7 @@
"x-appwrite": {
"method": "listMemberships",
"group": "memberships",
"weight": 226,
"weight": 229,
"cookies": false,
"type": "",
"deprecated": false,
@ -6987,7 +7072,7 @@
"x-appwrite": {
"method": "createMembership",
"group": "memberships",
"weight": 225,
"weight": 228,
"cookies": false,
"type": "",
"deprecated": false,
@ -7098,7 +7183,7 @@
"x-appwrite": {
"method": "getMembership",
"group": "memberships",
"weight": 227,
"weight": 230,
"cookies": false,
"type": "",
"deprecated": false,
@ -7170,7 +7255,7 @@
"x-appwrite": {
"method": "updateMembership",
"group": "memberships",
"weight": 228,
"weight": 231,
"cookies": false,
"type": "",
"deprecated": false,
@ -7257,7 +7342,7 @@
"x-appwrite": {
"method": "deleteMembership",
"group": "memberships",
"weight": 230,
"weight": 233,
"cookies": false,
"type": "",
"deprecated": false,
@ -7331,7 +7416,7 @@
"x-appwrite": {
"method": "updateMembershipStatus",
"group": "memberships",
"weight": 229,
"weight": 232,
"cookies": false,
"type": "",
"deprecated": false,
@ -7429,7 +7514,7 @@
"x-appwrite": {
"method": "getPrefs",
"group": "teams",
"weight": 221,
"weight": 224,
"cookies": false,
"type": "",
"deprecated": false,
@ -7490,7 +7575,7 @@
"x-appwrite": {
"method": "updatePrefs",
"group": "teams",
"weight": 223,
"weight": 226,
"cookies": false,
"type": "",
"deprecated": false,
@ -7572,7 +7657,7 @@
"x-appwrite": {
"method": "list",
"group": "files",
"weight": 436,
"weight": 438,
"cookies": false,
"type": "",
"deprecated": false,
@ -7638,7 +7723,7 @@
"tags": [
"tokens"
],
"description": "Create a new token. A token is linked to a file or a bucket and manages permissions for those file(s). Token can be passed as a header or request get parameter.",
"description": "Create a new token. A token is linked to a file. Token can be passed as a header or request get parameter.",
"responses": {
"201": {
"description": "ResourceToken",
@ -7654,12 +7739,12 @@
"x-appwrite": {
"method": "createFileToken",
"group": "files",
"weight": 433,
"weight": 436,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "tokens\/create-file-token.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a new token. A token is linked to a file or a bucket and manages permissions for those file(s). Token can be passed as a header or request get parameter.",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a new token. A token is linked to a file. Token can be passed as a header or request get parameter.",
"rate-limit": 60,
"rate-time": 60,
"rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}",
@ -7714,14 +7799,6 @@
"description": "Token expiry date",
"x-example": null,
"x-nullable": true
},
"permissions": {
"type": "array",
"description": "An array of permission strings. By default, only the current user is granted all permissions. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).",
"x-example": "[\"read(\"any\")\"]",
"items": {
"type": "string"
}
}
}
}
@ -7729,365 +7806,76 @@
}
}
}
},
"\/tokens\/{tokenId}": {
"get": {
"summary": "Get token",
"operationId": "tokensGet",
"tags": [
"tokens"
],
"description": "Get a token by its unique ID.",
"responses": {
"200": {
"description": "ResourceToken",
"content": {
"application\/json": {
"schema": {
"$ref": "#\/components\/schemas\/resourceToken"
}
}
}
}
},
"x-appwrite": {
"method": "get",
"group": "tokens",
"weight": 434,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "tokens\/get.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterGet a token by its unique ID.",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
"scope": "tokens.read",
"platforms": [
"client",
"server",
"server"
],
"packaging": false,
"auth": {
"Project": []
}
},
"security": [
{
"Project": [],
"Session": [],
"JWT": []
}
],
"parameters": [
{
"name": "tokenId",
"description": "Token ID.",
"required": true,
"schema": {
"type": "string",
"x-example": "<TOKEN_ID>"
},
"in": "path"
}
]
},
"patch": {
"summary": "Update token",
"operationId": "tokensUpdate",
"tags": [
"tokens"
],
"description": "Update a token by its unique ID. Use this endpoint to update a token's expiry date or permissions.",
"responses": {
"200": {
"description": "ResourceToken",
"content": {
"application\/json": {
"schema": {
"$ref": "#\/components\/schemas\/resourceToken"
}
}
}
}
},
"x-appwrite": {
"method": "update",
"group": "tokens",
"weight": 437,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "tokens\/update.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterUpdate a token by its unique ID. Use this endpoint to update a token's expiry date or permissions.",
"rate-limit": 60,
"rate-time": 60,
"rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}",
"scope": "tokens.write",
"platforms": [
"client",
"server",
"server"
],
"packaging": false,
"auth": {
"Project": []
}
},
"security": [
{
"Project": [],
"Session": [],
"JWT": []
}
],
"parameters": [
{
"name": "tokenId",
"description": "Token unique ID.",
"required": true,
"schema": {
"type": "string",
"x-example": "<TOKEN_ID>"
},
"in": "path"
}
],
"requestBody": {
"content": {
"application\/json": {
"schema": {
"type": "object",
"properties": {
"expire": {
"type": "string",
"description": "File token expiry date",
"x-example": null,
"x-nullable": true
},
"permissions": {
"type": "array",
"description": "An array of permission string. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).",
"x-example": "[\"read(\"any\")\"]",
"items": {
"type": "string"
}
}
}
}
}
}
}
},
"delete": {
"summary": "Delete token",
"operationId": "tokensDelete",
"tags": [
"tokens"
],
"description": "Delete a token by its unique ID.",
"responses": {
"204": {
"description": "No content"
}
},
"x-appwrite": {
"method": "delete",
"group": "tokens",
"weight": 438,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "tokens\/delete.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterDelete a token by its unique ID.",
"rate-limit": 60,
"rate-time": 60,
"rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}",
"scope": "tokens.write",
"platforms": [
"client",
"server",
"server"
],
"packaging": false,
"auth": {
"Project": []
}
},
"security": [
{
"Project": [],
"Session": [],
"JWT": []
}
],
"parameters": [
{
"name": "tokenId",
"description": "Token ID.",
"required": true,
"schema": {
"type": "string",
"x-example": "<TOKEN_ID>"
},
"in": "path"
}
]
}
},
"\/tokens\/{tokenId}\/jwt": {
"get": {
"summary": "Get token as JWT",
"operationId": "tokensGetJWT",
"tags": [
"tokens"
],
"description": "Get a JWT based token by its unique ID. You can use the JWT to authenticate on behalf of the user.",
"responses": {
"200": {
"description": "JWT",
"content": {
"application\/json": {
"schema": {
"$ref": "#\/components\/schemas\/jwt"
}
}
}
}
},
"x-appwrite": {
"method": "getJWT",
"group": "tokens",
"weight": 435,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "tokens\/get-j-w-t.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterGet a JWT based token by its unique ID. You can use the JWT to authenticate on behalf of the user.",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
"scope": "tokens.read",
"platforms": [
"client",
"server",
"server"
],
"packaging": false,
"auth": {
"Project": []
}
},
"security": [
{
"Project": [],
"Session": [],
"JWT": []
}
],
"parameters": [
{
"name": "tokenId",
"description": "File token ID.",
"required": true,
"schema": {
"type": "string",
"x-example": "<TOKEN_ID>"
},
"in": "path"
}
]
}
}
},
"tags": [
{
"name": "account",
"description": "The Account service allows you to authenticate and manage a user account.",
"x-globalAttributes": []
"description": "The Account service allows you to authenticate and manage a user account."
},
{
"name": "avatars",
"description": "The Avatars service aims to help you complete everyday tasks related to your app image, icons, and avatars.",
"x-globalAttributes": []
"description": "The Avatars service aims to help you complete everyday tasks related to your app image, icons, and avatars."
},
{
"name": "databases",
"description": "The Databases service allows you to create structured collections of documents, query and filter lists of documents",
"x-globalAttributes": [
"databaseId"
]
"description": "The Databases service allows you to create structured collections of documents, query and filter lists of documents"
},
{
"name": "locale",
"description": "The Locale service allows you to customize your app based on your users' location.",
"x-globalAttributes": []
"description": "The Locale service allows you to customize your app based on your users' location."
},
{
"name": "health",
"description": "The Health service allows you to both validate and monitor your Appwrite server's health.",
"x-globalAttributes": []
"description": "The Health service allows you to both validate and monitor your Appwrite server's health."
},
{
"name": "projects",
"description": "The Project service allows you to manage all the projects in your Appwrite server.",
"x-globalAttributes": []
"description": "The Project service allows you to manage all the projects in your Appwrite server."
},
{
"name": "project",
"description": "The Project service allows you to manage all the projects in your Appwrite server.",
"x-globalAttributes": []
"description": "The Project service allows you to manage all the projects in your Appwrite server."
},
{
"name": "storage",
"description": "The Storage service allows you to manage your project files.",
"x-globalAttributes": []
"description": "The Storage service allows you to manage your project files."
},
{
"name": "teams",
"description": "The Teams service allows you to group users of your project and to enable them to share read and write access to your project resources",
"x-globalAttributes": []
"description": "The Teams service allows you to group users of your project and to enable them to share read and write access to your project resources"
},
{
"name": "users",
"description": "The Users service allows you to manage your project users.",
"x-globalAttributes": []
"description": "The Users service allows you to manage your project users."
},
{
"name": "sites",
"description": "The Sites Service allows you view, create and manage your web applications.",
"x-globalAttributes": []
"description": "The Sites Service allows you view, create and manage your web applications."
},
{
"name": "functions",
"description": "The Functions Service allows you view, create and manage your Cloud Functions.",
"x-globalAttributes": []
"description": "The Functions Service allows you view, create and manage your Cloud Functions."
},
{
"name": "proxy",
"description": "The Proxy Service allows you to configure actions for your domains beyond DNS configuration.",
"x-globalAttributes": []
"description": "The Proxy Service allows you to configure actions for your domains beyond DNS configuration."
},
{
"name": "graphql",
"description": "The GraphQL API allows you to query and mutate your Appwrite server using GraphQL.",
"x-globalAttributes": []
"description": "The GraphQL API allows you to query and mutate your Appwrite server using GraphQL."
},
{
"name": "console",
"description": "The Console service allows you to interact with console relevant informations.",
"x-globalAttributes": []
"description": "The Console service allows you to interact with console relevant informations."
},
{
"name": "migrations",
"description": "The Migrations service allows you to migrate third-party data to your Appwrite project.",
"x-globalAttributes": []
"description": "The Migrations service allows you to migrate third-party data to your Appwrite project."
},
{
"name": "messaging",
"description": "The Messaging service allows you to send messages to any provider type (SMTP, push notification, SMS, etc.).",
"x-globalAttributes": []
"description": "The Messaging service allows you to send messages to any provider type (SMTP, push notification, SMS, etc.)."
}
],
"components": {
@ -9483,16 +9271,6 @@
"description": "Token creation date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"$permissions": {
"type": "array",
"description": "Token permissions. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).",
"items": {
"type": "string"
},
"x-example": [
"read(\"any\")"
]
},
"resourceId": {
"type": "string",
"description": "Resource ID.",
@ -9508,6 +9286,11 @@
"description": "Token expiration date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"secret": {
"type": "string",
"description": "JWT encoded string.",
"x-example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
},
"accessedAt": {
"type": "string",
"description": "Most recent access date in ISO 8601 format. This attribute is only updated again after 24 hours.",
@ -9517,10 +9300,10 @@
"required": [
"$id",
"$createdAt",
"$permissions",
"resourceId",
"resourceType",
"expire",
"secret",
"accessedAt"
]
},

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -2765,7 +2765,10 @@
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/account\/create-token-email.md",
"rate-limit": 10,
"rate-time": 3600,
"rate-key": "url:{url},email:{param-email}",
"rate-key": [
"url:{url},email:{param-email}",
"url:{url},ip:{ip}"
],
"scope": "sessions.write",
"platforms": [
"server",
@ -4571,7 +4574,7 @@
"tags": [
"databases"
],
"description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n",
"description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.",
"responses": {
"201": {
"description": "Document",
@ -4599,6 +4602,51 @@
"server"
],
"packaging": false,
"methods": [
{
"name": "createDocument",
"parameters": [
"databaseId",
"collectionId",
"documentId",
"data",
"permissions"
],
"required": [
"databaseId",
"collectionId",
"documentId",
"data"
],
"responses": [
{
"code": 201,
"model": "#\/definitions\/document"
}
],
"description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console."
},
{
"name": "createDocuments",
"parameters": [
"databaseId",
"collectionId",
"documents"
],
"required": [
"databaseId",
"collectionId",
"documents"
],
"responses": [
{
"code": 201,
"model": "#\/definitions\/documentList"
}
],
"description": "Create new Documents. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console."
}
],
"auth": {
"Project": []
}
@ -4636,13 +4684,13 @@
"documentId": {
"type": "string",
"description": "Document ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can't start with a special char. Max length is 36 chars.",
"default": null,
"default": "",
"x-example": "<DOCUMENT_ID>"
},
"data": {
"type": "object",
"description": "Document data as JSON object.",
"default": {},
"default": [],
"x-example": "{}"
},
"permissions": {
@ -4653,12 +4701,17 @@
"items": {
"type": "string"
}
},
"documents": {
"type": "array",
"description": "Array of documents data as JSON objects.",
"default": [],
"x-example": null,
"items": {
"type": "object"
}
}
},
"required": [
"documentId",
"data"
]
}
}
}
]
@ -4874,7 +4927,7 @@
"x-appwrite": {
"method": "deleteDocument",
"group": "documents",
"weight": 114,
"weight": 116,
"cookies": false,
"type": "",
"deprecated": false,
@ -4952,7 +5005,7 @@
"x-appwrite": {
"method": "listExecutions",
"group": "executions",
"weight": 388,
"weight": 391,
"cookies": false,
"type": "",
"deprecated": false,
@ -5026,7 +5079,7 @@
"x-appwrite": {
"method": "createExecution",
"group": "executions",
"weight": 386,
"weight": 389,
"cookies": false,
"type": "",
"deprecated": false,
@ -5143,7 +5196,7 @@
"x-appwrite": {
"method": "getExecution",
"group": "executions",
"weight": 387,
"weight": 390,
"cookies": false,
"type": "",
"deprecated": false,
@ -5215,7 +5268,7 @@
"x-appwrite": {
"method": "query",
"group": "graphql",
"weight": 302,
"weight": 305,
"cookies": false,
"type": "graphql",
"deprecated": false,
@ -5289,7 +5342,7 @@
"x-appwrite": {
"method": "mutation",
"group": "graphql",
"weight": 301,
"weight": 304,
"cookies": false,
"type": "graphql",
"deprecated": false,
@ -5361,7 +5414,7 @@
"x-appwrite": {
"method": "get",
"group": null,
"weight": 118,
"weight": 121,
"cookies": false,
"type": "",
"deprecated": false,
@ -5413,7 +5466,7 @@
"x-appwrite": {
"method": "listCodes",
"group": null,
"weight": 119,
"weight": 122,
"cookies": false,
"type": "",
"deprecated": false,
@ -5465,7 +5518,7 @@
"x-appwrite": {
"method": "listContinents",
"group": null,
"weight": 123,
"weight": 126,
"cookies": false,
"type": "",
"deprecated": false,
@ -5517,7 +5570,7 @@
"x-appwrite": {
"method": "listCountries",
"group": null,
"weight": 120,
"weight": 123,
"cookies": false,
"type": "",
"deprecated": false,
@ -5569,7 +5622,7 @@
"x-appwrite": {
"method": "listCountriesEU",
"group": null,
"weight": 121,
"weight": 124,
"cookies": false,
"type": "",
"deprecated": false,
@ -5621,7 +5674,7 @@
"x-appwrite": {
"method": "listCountriesPhones",
"group": null,
"weight": 122,
"weight": 125,
"cookies": false,
"type": "",
"deprecated": false,
@ -5673,7 +5726,7 @@
"x-appwrite": {
"method": "listCurrencies",
"group": null,
"weight": 124,
"weight": 127,
"cookies": false,
"type": "",
"deprecated": false,
@ -5725,7 +5778,7 @@
"x-appwrite": {
"method": "listLanguages",
"group": null,
"weight": 125,
"weight": 128,
"cookies": false,
"type": "",
"deprecated": false,
@ -5779,7 +5832,7 @@
"x-appwrite": {
"method": "createSubscriber",
"group": "subscribers",
"weight": 348,
"weight": 351,
"cookies": false,
"type": "",
"deprecated": false,
@ -5864,7 +5917,7 @@
"x-appwrite": {
"method": "deleteSubscriber",
"group": "subscribers",
"weight": 352,
"weight": 355,
"cookies": false,
"type": "",
"deprecated": false,
@ -5935,7 +5988,7 @@
"x-appwrite": {
"method": "listFiles",
"group": "files",
"weight": 208,
"weight": 211,
"cookies": false,
"type": "",
"deprecated": false,
@ -6018,7 +6071,7 @@
"x-appwrite": {
"method": "createFile",
"group": "files",
"weight": 207,
"weight": 210,
"cookies": false,
"type": "upload",
"deprecated": false,
@ -6108,7 +6161,7 @@
"x-appwrite": {
"method": "getFile",
"group": "files",
"weight": 209,
"weight": 212,
"cookies": false,
"type": "",
"deprecated": false,
@ -6178,7 +6231,7 @@
"x-appwrite": {
"method": "updateFile",
"group": "files",
"weight": 214,
"weight": 217,
"cookies": false,
"type": "",
"deprecated": false,
@ -6267,7 +6320,7 @@
"x-appwrite": {
"method": "deleteFile",
"group": "files",
"weight": 215,
"weight": 218,
"cookies": false,
"type": "",
"deprecated": false,
@ -6337,7 +6390,7 @@
"x-appwrite": {
"method": "getFileDownload",
"group": "files",
"weight": 211,
"weight": 214,
"cookies": false,
"type": "location",
"deprecated": false,
@ -6380,6 +6433,15 @@
"type": "string",
"x-example": "<FILE_ID>",
"in": "path"
},
{
"name": "token",
"description": "File token for accessing this file.",
"required": false,
"type": "string",
"x-example": "<TOKEN>",
"default": "",
"in": "query"
}
]
}
@ -6407,7 +6469,7 @@
"x-appwrite": {
"method": "getFilePreview",
"group": "files",
"weight": 210,
"weight": 213,
"cookies": false,
"type": "location",
"deprecated": false,
@ -6578,6 +6640,15 @@
"x-enum-keys": [],
"default": "",
"in": "query"
},
{
"name": "token",
"description": "File token for accessing this file.",
"required": false,
"type": "string",
"x-example": "<TOKEN>",
"default": "",
"in": "query"
}
]
}
@ -6605,7 +6676,7 @@
"x-appwrite": {
"method": "getFileView",
"group": "files",
"weight": 212,
"weight": 215,
"cookies": false,
"type": "location",
"deprecated": false,
@ -6648,6 +6719,15 @@
"type": "string",
"x-example": "<FILE_ID>",
"in": "path"
},
{
"name": "token",
"description": "File token for accessing this file.",
"required": false,
"type": "string",
"x-example": "<TOKEN>",
"default": "",
"in": "query"
}
]
}
@ -6675,7 +6755,7 @@
"x-appwrite": {
"method": "list",
"group": "teams",
"weight": 219,
"weight": 222,
"cookies": false,
"type": "",
"deprecated": false,
@ -6750,7 +6830,7 @@
"x-appwrite": {
"method": "create",
"group": "teams",
"weight": 218,
"weight": 221,
"cookies": false,
"type": "",
"deprecated": false,
@ -6840,7 +6920,7 @@
"x-appwrite": {
"method": "get",
"group": "teams",
"weight": 220,
"weight": 223,
"cookies": false,
"type": "",
"deprecated": false,
@ -6902,7 +6982,7 @@
"x-appwrite": {
"method": "updateName",
"group": "teams",
"weight": 222,
"weight": 225,
"cookies": false,
"type": "",
"deprecated": false,
@ -6977,7 +7057,7 @@
"x-appwrite": {
"method": "delete",
"group": "teams",
"weight": 224,
"weight": 227,
"cookies": false,
"type": "",
"deprecated": false,
@ -7039,7 +7119,7 @@
"x-appwrite": {
"method": "listMemberships",
"group": "memberships",
"weight": 226,
"weight": 229,
"cookies": false,
"type": "",
"deprecated": false,
@ -7122,7 +7202,7 @@
"x-appwrite": {
"method": "createMembership",
"group": "memberships",
"weight": 225,
"weight": 228,
"cookies": false,
"type": "",
"deprecated": false,
@ -7235,7 +7315,7 @@
"x-appwrite": {
"method": "getMembership",
"group": "memberships",
"weight": 227,
"weight": 230,
"cookies": false,
"type": "",
"deprecated": false,
@ -7305,7 +7385,7 @@
"x-appwrite": {
"method": "updateMembership",
"group": "memberships",
"weight": 228,
"weight": 231,
"cookies": false,
"type": "",
"deprecated": false,
@ -7391,7 +7471,7 @@
"x-appwrite": {
"method": "deleteMembership",
"group": "memberships",
"weight": 230,
"weight": 233,
"cookies": false,
"type": "",
"deprecated": false,
@ -7463,7 +7543,7 @@
"x-appwrite": {
"method": "updateMembershipStatus",
"group": "memberships",
"weight": 229,
"weight": 232,
"cookies": false,
"type": "",
"deprecated": false,
@ -7557,7 +7637,7 @@
"x-appwrite": {
"method": "getPrefs",
"group": "teams",
"weight": 221,
"weight": 224,
"cookies": false,
"type": "",
"deprecated": false,
@ -7618,7 +7698,7 @@
"x-appwrite": {
"method": "updatePrefs",
"group": "teams",
"weight": 223,
"weight": 226,
"cookies": false,
"type": "",
"deprecated": false,
@ -7697,7 +7777,7 @@
"x-appwrite": {
"method": "list",
"group": "files",
"weight": 436,
"weight": 438,
"cookies": false,
"type": "",
"deprecated": false,
@ -7767,7 +7847,7 @@
"tags": [
"tokens"
],
"description": "Create a new token. A token is linked to a file or a bucket and manages permissions for those file(s). Token can be passed as a header or request get parameter.",
"description": "Create a new token. A token is linked to a file. Token can be passed as a header or request get parameter.",
"responses": {
"201": {
"description": "ResourceToken",
@ -7779,12 +7859,12 @@
"x-appwrite": {
"method": "createFileToken",
"group": "files",
"weight": 433,
"weight": 436,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "tokens\/create-file-token.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a new token. A token is linked to a file or a bucket and manages permissions for those file(s). Token can be passed as a header or request get parameter.",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a new token. A token is linked to a file. Token can be passed as a header or request get parameter.",
"rate-limit": 60,
"rate-time": 60,
"rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}",
@ -7835,378 +7915,82 @@
"default": null,
"x-example": null,
"x-nullable": true
},
"permissions": {
"type": "array",
"description": "An array of permission strings. By default, only the current user is granted all permissions. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).",
"default": [],
"x-example": "[\"read(\"any\")\"]",
"items": {
"type": "string"
}
}
}
}
}
]
}
},
"\/tokens\/{tokenId}": {
"get": {
"summary": "Get token",
"operationId": "tokensGet",
"consumes": [],
"produces": [
"application\/json"
],
"tags": [
"tokens"
],
"description": "Get a token by its unique ID.",
"responses": {
"200": {
"description": "ResourceToken",
"schema": {
"$ref": "#\/definitions\/resourceToken"
}
}
},
"x-appwrite": {
"method": "get",
"group": "tokens",
"weight": 434,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "tokens\/get.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterGet a token by its unique ID.",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
"scope": "tokens.read",
"platforms": [
"client",
"server",
"server"
],
"packaging": false,
"auth": {
"Project": []
}
},
"security": [
{
"Project": [],
"Session": [],
"JWT": []
}
],
"parameters": [
{
"name": "tokenId",
"description": "Token ID.",
"required": true,
"type": "string",
"x-example": "<TOKEN_ID>",
"in": "path"
}
]
},
"patch": {
"summary": "Update token",
"operationId": "tokensUpdate",
"consumes": [
"application\/json"
],
"produces": [
"application\/json"
],
"tags": [
"tokens"
],
"description": "Update a token by its unique ID. Use this endpoint to update a token's expiry date or permissions.",
"responses": {
"200": {
"description": "ResourceToken",
"schema": {
"$ref": "#\/definitions\/resourceToken"
}
}
},
"x-appwrite": {
"method": "update",
"group": "tokens",
"weight": 437,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "tokens\/update.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterUpdate a token by its unique ID. Use this endpoint to update a token's expiry date or permissions.",
"rate-limit": 60,
"rate-time": 60,
"rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}",
"scope": "tokens.write",
"platforms": [
"client",
"server",
"server"
],
"packaging": false,
"auth": {
"Project": []
}
},
"security": [
{
"Project": [],
"Session": [],
"JWT": []
}
],
"parameters": [
{
"name": "tokenId",
"description": "Token unique ID.",
"required": true,
"type": "string",
"x-example": "<TOKEN_ID>",
"in": "path"
},
{
"name": "payload",
"in": "body",
"schema": {
"type": "object",
"properties": {
"expire": {
"type": "string",
"description": "File token expiry date",
"default": null,
"x-example": null,
"x-nullable": true
},
"permissions": {
"type": "array",
"description": "An array of permission string. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).",
"default": null,
"x-example": "[\"read(\"any\")\"]",
"items": {
"type": "string"
}
}
}
}
}
]
},
"delete": {
"summary": "Delete token",
"operationId": "tokensDelete",
"consumes": [
"application\/json"
],
"produces": [],
"tags": [
"tokens"
],
"description": "Delete a token by its unique ID.",
"responses": {
"204": {
"description": "No content"
}
},
"x-appwrite": {
"method": "delete",
"group": "tokens",
"weight": 438,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "tokens\/delete.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterDelete a token by its unique ID.",
"rate-limit": 60,
"rate-time": 60,
"rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}",
"scope": "tokens.write",
"platforms": [
"client",
"server",
"server"
],
"packaging": false,
"auth": {
"Project": []
}
},
"security": [
{
"Project": [],
"Session": [],
"JWT": []
}
],
"parameters": [
{
"name": "tokenId",
"description": "Token ID.",
"required": true,
"type": "string",
"x-example": "<TOKEN_ID>",
"in": "path"
}
]
}
},
"\/tokens\/{tokenId}\/jwt": {
"get": {
"summary": "Get token as JWT",
"operationId": "tokensGetJWT",
"consumes": [],
"produces": [
"application\/json"
],
"tags": [
"tokens"
],
"description": "Get a JWT based token by its unique ID. You can use the JWT to authenticate on behalf of the user.",
"responses": {
"200": {
"description": "JWT",
"schema": {
"$ref": "#\/definitions\/jwt"
}
}
},
"x-appwrite": {
"method": "getJWT",
"group": "tokens",
"weight": 435,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "tokens\/get-j-w-t.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterGet a JWT based token by its unique ID. You can use the JWT to authenticate on behalf of the user.",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
"scope": "tokens.read",
"platforms": [
"client",
"server",
"server"
],
"packaging": false,
"auth": {
"Project": []
}
},
"security": [
{
"Project": [],
"Session": [],
"JWT": []
}
],
"parameters": [
{
"name": "tokenId",
"description": "File token ID.",
"required": true,
"type": "string",
"x-example": "<TOKEN_ID>",
"in": "path"
}
]
}
}
},
"tags": [
{
"name": "account",
"description": "The Account service allows you to authenticate and manage a user account.",
"x-globalAttributes": []
"description": "The Account service allows you to authenticate and manage a user account."
},
{
"name": "avatars",
"description": "The Avatars service aims to help you complete everyday tasks related to your app image, icons, and avatars.",
"x-globalAttributes": []
"description": "The Avatars service aims to help you complete everyday tasks related to your app image, icons, and avatars."
},
{
"name": "databases",
"description": "The Databases service allows you to create structured collections of documents, query and filter lists of documents",
"x-globalAttributes": [
"databaseId"
]
"description": "The Databases service allows you to create structured collections of documents, query and filter lists of documents"
},
{
"name": "locale",
"description": "The Locale service allows you to customize your app based on your users' location.",
"x-globalAttributes": []
"description": "The Locale service allows you to customize your app based on your users' location."
},
{
"name": "health",
"description": "The Health service allows you to both validate and monitor your Appwrite server's health.",
"x-globalAttributes": []
"description": "The Health service allows you to both validate and monitor your Appwrite server's health."
},
{
"name": "projects",
"description": "The Project service allows you to manage all the projects in your Appwrite server.",
"x-globalAttributes": []
"description": "The Project service allows you to manage all the projects in your Appwrite server."
},
{
"name": "project",
"description": "The Project service allows you to manage all the projects in your Appwrite server.",
"x-globalAttributes": []
"description": "The Project service allows you to manage all the projects in your Appwrite server."
},
{
"name": "storage",
"description": "The Storage service allows you to manage your project files.",
"x-globalAttributes": []
"description": "The Storage service allows you to manage your project files."
},
{
"name": "teams",
"description": "The Teams service allows you to group users of your project and to enable them to share read and write access to your project resources",
"x-globalAttributes": []
"description": "The Teams service allows you to group users of your project and to enable them to share read and write access to your project resources"
},
{
"name": "users",
"description": "The Users service allows you to manage your project users.",
"x-globalAttributes": []
"description": "The Users service allows you to manage your project users."
},
{
"name": "sites",
"description": "The Sites Service allows you view, create and manage your web applications.",
"x-globalAttributes": []
"description": "The Sites Service allows you view, create and manage your web applications."
},
{
"name": "functions",
"description": "The Functions Service allows you view, create and manage your Cloud Functions.",
"x-globalAttributes": []
"description": "The Functions Service allows you view, create and manage your Cloud Functions."
},
{
"name": "proxy",
"description": "The Proxy Service allows you to configure actions for your domains beyond DNS configuration.",
"x-globalAttributes": []
"description": "The Proxy Service allows you to configure actions for your domains beyond DNS configuration."
},
{
"name": "graphql",
"description": "The GraphQL API allows you to query and mutate your Appwrite server using GraphQL.",
"x-globalAttributes": []
"description": "The GraphQL API allows you to query and mutate your Appwrite server using GraphQL."
},
{
"name": "console",
"description": "The Console service allows you to interact with console relevant informations.",
"x-globalAttributes": []
"description": "The Console service allows you to interact with console relevant informations."
},
{
"name": "migrations",
"description": "The Migrations service allows you to migrate third-party data to your Appwrite project.",
"x-globalAttributes": []
"description": "The Migrations service allows you to migrate third-party data to your Appwrite project."
},
{
"name": "messaging",
"description": "The Messaging service allows you to send messages to any provider type (SMTP, push notification, SMS, etc.).",
"x-globalAttributes": []
"description": "The Messaging service allows you to send messages to any provider type (SMTP, push notification, SMS, etc.)."
}
],
"definitions": {
@ -9586,16 +9370,6 @@
"description": "Token creation date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"$permissions": {
"type": "array",
"description": "Token permissions. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).",
"items": {
"type": "string"
},
"x-example": [
"read(\"any\")"
]
},
"resourceId": {
"type": "string",
"description": "Resource ID.",
@ -9611,6 +9385,11 @@
"description": "Token expiration date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"secret": {
"type": "string",
"description": "JWT encoded string.",
"x-example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
},
"accessedAt": {
"type": "string",
"description": "Most recent access date in ISO 8601 format. This attribute is only updated again after 24 hours.",
@ -9620,10 +9399,10 @@
"required": [
"$id",
"$createdAt",
"$permissions",
"resourceId",
"resourceType",
"expire",
"secret",
"accessedAt"
]
},

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -951,12 +951,14 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
->param('rotation', 0, new Range(-360, 360), 'Preview image rotation in degrees. Pass an integer between -360 and 360.', true)
->param('background', '', new HexColor(), 'Preview image background color. Only works with transparent images (png). Use a valid HEX color, no # is needed for prefix.', true)
->param('output', '', new WhiteList(\array_keys(Config::getParam('storage-outputs')), true), 'Output format type (jpeg, jpg, png, gif and webp).', true)
// NOTE: this is only for the sdk generator and is not used in the action below and is utilised in `resources.php` for `resourceToken`.
->param('token', '', new Text(512), 'File token for accessing this file.', true)
->inject('response')
->inject('dbForProject')
->inject('resourceToken')
->inject('deviceForFiles')
->inject('deviceForLocal')
->action(function (string $bucketId, string $fileId, int $width, int $height, string $gravity, int $quality, int $borderWidth, string $borderColor, int $borderRadius, float $opacity, int $rotation, string $background, string $output, Response $response, Database $dbForProject, Document $resourceToken, Device $deviceForFiles, Device $deviceForLocal) {
->action(function (string $bucketId, string $fileId, int $width, int $height, string $gravity, int $quality, int $borderWidth, string $borderColor, int $borderRadius, float $opacity, int $rotation, string $background, string $output, ?string $token, Response $response, Database $dbForProject, Document $resourceToken, Device $deviceForFiles, Device $deviceForLocal) {
if (!\extension_loaded('imagick')) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing');
@ -1129,13 +1131,15 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
))
->param('bucketId', '', new UID(), 'Storage bucket ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket).')
->param('fileId', '', new UID(), 'File ID.')
// NOTE: this is only for the sdk generator and is not used in the action below and is utilised in `resources.php` for `resourceToken`.
->param('token', '', new Text(512), 'File token for accessing this file.', true)
->inject('request')
->inject('response')
->inject('dbForProject')
->inject('mode')
->inject('resourceToken')
->inject('deviceForFiles')
->action(function (string $bucketId, string $fileId, Request $request, Response $response, Database $dbForProject, string $mode, Document $resourceToken, Device $deviceForFiles) {
->action(function (string $bucketId, string $fileId, ?string $token, Request $request, Response $response, Database $dbForProject, string $mode, Document $resourceToken, Device $deviceForFiles) {
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
@ -1284,13 +1288,15 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
))
->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket).')
->param('fileId', '', new UID(), 'File ID.')
// NOTE: this is only for the sdk generator and is not used in the action below and is utilised in `resources.php` for `resourceToken`.
->param('token', '', new Text(512), 'File token for accessing this file.', true)
->inject('response')
->inject('request')
->inject('dbForProject')
->inject('mode')
->inject('resourceToken')
->inject('deviceForFiles')
->action(function (string $bucketId, string $fileId, Response $response, Request $request, Database $dbForProject, string $mode, Document $resourceToken, Device $deviceForFiles) {
->action(function (string $bucketId, string $fileId, ?string $token, Response $response, Request $request, Database $dbForProject, string $mode, Document $resourceToken, Device $deviceForFiles) {
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());

View file

@ -918,38 +918,52 @@ App::setResource('resourceToken', function ($project, $dbForProject, $request) {
}
$tokenId = $payload['tokenId'] ?? '';
$secret = $payload['secret'] ?? '';
if (empty($tokenId) || empty($secret)) {
if (empty($tokenId)) {
return new Document([]);
}
$token = Authorization::skip(fn () => $dbForProject->getDocument('resourceTokens', $tokenId));
if ($token->isEmpty() || $token->getAttribute('secret') !== $secret) {
if ($token->isEmpty()) {
return new Document([]);
}
if ($token->getAttribute('resourceType') === TOKENS_RESOURCE_TYPE_FILES) {
$internalIds = explode(':', $token->getAttribute('resourceInternalId'));
$ids = explode(':', $token->getAttribute('resourceId'));
$expiry = $token->getAttribute('expire');
if (count($internalIds) !== 2 || count($ids) !== 2) {
if ($expiry !== null) {
$now = new \DateTime();
$expiryDate = new \DateTime($expiry);
if ($expiryDate < $now) {
return new Document([]);
}
$accessedAt = $token->getAttribute('accessedAt', 0);
if (empty($accessedAt) || DatabaseDateTime::formatTz(DatabaseDateTime::addSeconds(new \DateTime(), - APP_RESOURCE_TOKEN_ACCESS)) > $accessedAt) {
$token->setAttribute('accessedAt', DatabaseDateTime::now());
Authorization::skip(fn () => $dbForProject->updateDocument('resourceTokens', $token->getId(), $token));
}
return new Document([
'bucketId' => $ids[0],
'fileId' => $ids[1],
'bucketInternalId' => $internalIds[0],
'fileInternalId' => $internalIds[1],
]);
}
return match ($token->getAttribute('resourceType')) {
TOKENS_RESOURCE_TYPE_FILES => (function () use ($token, $dbForProject) {
$internalIds = explode(':', $token->getAttribute('resourceInternalId'));
$ids = explode(':', $token->getAttribute('resourceId'));
if (count($internalIds) !== 2 || count($ids) !== 2) {
return new Document([]);
}
$accessedAt = $token->getAttribute('accessedAt', 0);
if (empty($accessedAt) || DatabaseDateTime::formatTz(DatabaseDateTime::addSeconds(new \DateTime(), - APP_RESOURCE_TOKEN_ACCESS)) > $accessedAt) {
$token->setAttribute('accessedAt', DatabaseDateTime::now());
Authorization::skip(fn () => $dbForProject->updateDocument('resourceTokens', $token->getId(), $token));
}
return new Document([
'bucketId' => $ids[0],
'fileId' => $ids[1],
'bucketInternalId' => $internalIds[0],
'fileInternalId' => $internalIds[1],
]);
})(),
default => throw new Exception(Exception::TOKEN_RESOURCE_TYPE_INVALID),
};
}
return new Document([]);
}, ['project', 'dbForProject', 'request']);

View file

@ -322,7 +322,7 @@ class Exception extends \Exception
/** Tokens */
public const TOKEN_NOT_FOUND = 'token_not_found';
public const TOKEN_EXPIRED = 'token_expired';
public const TOKEN_RESOURCE_TYPE_INVALID = 'token_resource_type_invalid';
protected string $type = '';
protected array $errors = [];

View file

@ -15,7 +15,6 @@ use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
use Utopia\Database\Validator\Permissions;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Scope\HTTP;
use Utopia\Validator\Nullable;
@ -49,7 +48,7 @@ class Create extends Action
group: 'files',
name: 'createFileToken',
description: <<<EOT
Create a new token. A token is linked to a file or a bucket and manages permissions for those file(s). Token can be passed as a header or request get parameter.
Create a new token. A token is linked to a file. Token can be passed as a header or request get parameter.
EOT,
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
responses: [
@ -63,15 +62,13 @@ class Create extends Action
->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket).')
->param('fileId', '', new UID(), 'File unique ID.')
->param('expire', null, new Nullable(new DatetimeValidator()), 'Token expiry date', true)
->param('permissions', [], new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permission strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
->inject('response')
->inject('dbForProject')
->inject('user')
->inject('queueForEvents')
->callback([$this, 'action']);
}
public function action(string $bucketId, string $fileId, ?string $expire, ?array $permissions, Response $response, Database $dbForProject, Document $user, Event $queueForEvents)
public function action(string $bucketId, string $fileId, ?string $expire, Response $response, Database $dbForProject, Event $queueForEvents): void
{
/**
@ -100,7 +97,6 @@ class Create extends Action
'resourceInternalId' => $bucket->getInternalId() . ':' . $file->getInternalId(),
'resourceType' => TOKENS_RESOURCE_TYPE_FILES,
'expire' => $expire,
'$permissions' => $permissions
]));
$queueForEvents

View file

@ -45,7 +45,7 @@ class Delete extends Action
description: <<<EOT
Delete a token by its unique ID.
EOT,
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_NOCONTENT,

View file

@ -38,7 +38,7 @@ class Get extends Action
description: <<<EOT
Get a token by its unique ID.
EOT,
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,

View file

@ -1,91 +0,0 @@
<?php
namespace Appwrite\Platform\Modules\Tokens\Http\Tokens\JWT;
use Ahc\Jwt\JWT;
use Appwrite\Extend\Exception;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\System\System;
class Get extends Action
{
use HTTP;
public static function getName()
{
return 'getTokenJWT';
}
public function __construct()
{
$this->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET)
->setHttpPath('/v1/tokens/:tokenId/jwt')
->desc('Get token as JWT')
->groups(['api', 'tokens'])
->label('scope', 'tokens.read')
->label('usage.metric', 'tokens.{scope}.requests.read')
->label('usage.params', ['tokenId:{request.tokenId}'])
->label('sdk', new Method(
namespace: 'tokens',
group: 'tokens',
name: 'getJWT',
description: <<<EOT
Get a JWT based token by its unique ID. You can use the JWT to authenticate on behalf of the user.
EOT,
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_JWT,
)
],
contentType: ContentType::JSON
))
->param('tokenId', '', new UID(), 'File token ID.')
->inject('response')
->inject('dbForProject')
->callback([$this, 'action']);
}
public function action(string $tokenId, Response $response, Database $dbForProject)
{
$token = $dbForProject->getDocument('resourceTokens', $tokenId);
if ($token->isEmpty()) {
throw new Exception(Exception::TOKEN_NOT_FOUND);
}
// calculate maxAge based on expiry date
$maxAge = PHP_INT_MAX;
$expire = $token->getAttribute('expire');
if ($expire !== null) {
$now = new \DateTime();
$expiryDate = new \DateTime($expire);
if ($expiryDate < $now) {
throw new Exception(Exception::TOKEN_EXPIRED);
}
$maxAge = $expiryDate->getTimestamp() - $now->getTimestamp();
}
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $maxAge, 10); // Instantiate with key, algo, maxAge and leeway.
$response
->setStatusCode(Response::STATUS_CODE_OK)
->dynamic(new Document(['jwt' => $jwt->encode([
'resourceType' => $token->getAttribute('resourceType'),
'resourceId' => $token->getAttribute('resourceId'),
'resourceInternalId' => $token->getAttribute('resourceInternalId'),
'tokenId' => $token->getId(),
'secret' => $token->getAttribute('secret')
])]), Response::MODEL_JWT);
}
}

View file

@ -2,7 +2,6 @@
namespace Appwrite\Platform\Modules\Tokens\Http\Tokens;
use Appwrite\Auth\Auth;
use Appwrite\Event\Event;
use Appwrite\Extend\Exception;
use Appwrite\SDK\AuthType;
@ -11,11 +10,7 @@ use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response;
use Utopia\Database\Database;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
use Utopia\Database\Validator\Permissions;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
@ -50,9 +45,9 @@ class Update extends Action
group: 'tokens',
name: 'update',
description: <<<EOT
Update a token by its unique ID. Use this endpoint to update a token's expiry date or permissions.
Update a token by its unique ID. Use this endpoint to update a token's expiry date.
EOT,
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
@ -63,14 +58,13 @@ class Update extends Action
))
->param('tokenId', '', new UID(), 'Token unique ID.')
->param('expire', null, new Nullable(new DatetimeValidator()), 'File token expiry date', true)
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permission string. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->callback([$this, 'action']);
}
public function action(string $tokenId, ?string $expire, ?array $permissions, Response $response, Database $dbForProject, Event $queueForEvents)
public function action(string $tokenId, ?string $expire, Response $response, Database $dbForProject, Event $queueForEvents)
{
$token = $dbForProject->getDocument('resourceTokens', $tokenId);
@ -78,47 +72,11 @@ class Update extends Action
throw new Exception(Exception::TOKEN_NOT_FOUND);
}
// Map aggregate permissions into the multiple permissions they represent.
$permissions = Permission::aggregate($permissions, [
Database::PERMISSION_READ,
Database::PERMISSION_UPDATE,
Database::PERMISSION_DELETE,
]);
// Users can only manage their own roles, API keys and Admin users can manage any
$roles = Authorization::getRoles();
if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles) && !\is_null($permissions)) {
foreach (Database::PERMISSIONS as $type) {
foreach ($permissions as $permission) {
$permission = Permission::parse($permission);
if ($permission->getPermission() != $type) {
continue;
}
$role = (new Role(
$permission->getRole(),
$permission->getIdentifier(),
$permission->getDimension()
))->toString();
if (!Authorization::isRole($role)) {
throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')');
}
}
}
}
if (\is_null($permissions)) {
$permissions = $token->getPermissions() ?? [];
}
$token
->setAttribute('expire', $expire)
->setAttribute('$permissions', $permissions);
$token->setAttribute('expire', $expire);
$token = $dbForProject->updateDocument('resourceTokens', $tokenId, $token);
$queueForEvents
->setParam('tokenId', $token->getId())
;
$queueForEvents->setParam('tokenId', $token->getId());
$response->dynamic($token, Response::MODEL_RESOURCE_TOKEN);
}

View file

@ -6,7 +6,6 @@ use Appwrite\Platform\Modules\Tokens\Http\Tokens\Buckets\Files\Create as CreateF
use Appwrite\Platform\Modules\Tokens\Http\Tokens\Buckets\Files\XList as ListFileTokens;
use Appwrite\Platform\Modules\Tokens\Http\Tokens\Delete as DeleteToken;
use Appwrite\Platform\Modules\Tokens\Http\Tokens\Get as GetToken;
use Appwrite\Platform\Modules\Tokens\Http\Tokens\JWT\Get as GetTokenJWT;
use Appwrite\Platform\Modules\Tokens\Http\Tokens\Update as UpdateToken;
use Utopia\Platform\Service;
@ -18,7 +17,6 @@ class Http extends Service
$this
->addAction(CreateFileToken::getName(), new CreateFileToken())
->addAction(GetToken::getName(), new GetToken())
->addAction(GetTokenJWT::getName(), new GetTokenJWT())
->addAction(ListFileTokens::getName(), new ListFileTokens())
->addAction(UpdateToken::getName(), new UpdateToken())
->addAction(DeleteToken::getName(), new DeleteToken())

View file

@ -2,8 +2,11 @@
namespace Appwrite\Utopia\Response\Model;
use Ahc\Jwt\JWT;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
use Utopia\Database\Document;
use Utopia\System\System;
class ResourceToken extends Model
{
@ -22,13 +25,6 @@ class ResourceToken extends Model
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])
->addRule('$permissions', [
'type' => self::TYPE_STRING,
'description' => 'Token permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).',
'default' => '',
'example' => ['read("any")'],
'array' => true,
])
->addRule('resourceId', [
'type' => self::TYPE_STRING,
'description' => 'Resource ID.',
@ -47,6 +43,13 @@ class ResourceToken extends Model
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])
->addRule('secret', [
'type' => self::TYPE_STRING,
'description' => 'JWT encoded string.',
'default' => '',
// this is a secret but is converted to a JWT token when sent back to the client after filter.
'example' => 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c',
])
->addRule('accessedAt', [
'type' => self::TYPE_DATETIME,
'description' => 'Most recent access date in ISO 8601 format. This attribute is only updated again after ' . APP_RESOURCE_TOKEN_ACCESS / 60 / 60 . ' hours.',
@ -56,6 +59,32 @@ class ResourceToken extends Model
;
}
public function filter(Document $document): Document
{
$maxAge = PHP_INT_MAX;
$expire = $document->getAttribute('expire');
if ($expire !== null) {
$now = new \DateTime();
$expiryDate = new \DateTime($expire);
// set 1 min if expired, we check for expiry later on route hooks for validation!
$maxAge = min(60, $expiryDate->getTimestamp() - $now->getTimestamp());
}
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $maxAge, 10);
$secret = $jwt->encode([
'tokenId' => $document->getId(),
'resourceId' => $document->getAttribute('resourceId'),
'resourceType' => $document->getAttribute('resourceType'),
'resourceInternalId' => $document->getAttribute('resourceInternalId'),
]);
$document->setAttribute('secret', $secret);
return $document;
}
/**
* Get Name
*

View file

@ -66,7 +66,7 @@ trait TokensBase
return [
'fileId' => $fileId,
'bucketId' => $bucketId,
'tokenId' => $token['body']['$id'],
'token' => $token['body'],
'guestHeaders' => [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
@ -143,22 +143,12 @@ trait TokensBase
*/
public function testPreviewFileWithToken(array $data): array
{
$token = $data['token'];
$fileId = $data['fileId'];
$tokenId = $data['tokenId'];
$bucketId = $data['bucketId'];
$guestHeaders = $data['guestHeaders'];
$adminHeaders = array_merge($guestHeaders, ['x-appwrite-key' => $this->getProject()['apiKey']]);
// Generate JWT as an admin user.
$tokenJWT = $this->client->call(
Client::METHOD_GET,
'/tokens/' . $tokenId . '/jwt/',
$adminHeaders
);
$this->assertEquals(200, $tokenJWT['headers']['status-code']);
$this->assertArrayHasKey('jwt', $tokenJWT['body']);
$tokenJWT = $tokenJWT['body']['jwt'];
$tokenJWT = $token['secret'];
// Generate a preview
$filePreview = $this->client->call(