@dodi-smart/ttlock-graphql-api
v1.3.0
Published
TTLock GraphQL API
Downloads
18
Maintainers
Readme
This package creates a TTLock GraphQL API, allowing for interaction with data at TTLock. It is unofficial TTLock GraphQL API generated based on documentation, mainly configured to be used as Hasura Remote Schema.
Here's an example of how to use the TTLock GraphQL API to get a list of smart locks of the configured TTLock user:
query MyLocks {
ttlock {
smartlocks {
lockName
lockId
details {
state {
state
}
electricQuantity
firmwareRevision
hardwareRevision
}
}
}
}
It's recommended to add the TTLock GraphQL API as a Remote Schema in Hasura and connect data from your database with data in TTLock. By doing so, it's possible to request data from your database and TTLock in a single GraphQL query.
Here's an example of how to use the TTLock GraphQL API to get a list of smart locks for a specific TTLock user. Note that the user data is fetched from your database and the TTLock user data is fetched trough the TTLock API:
query UsersLocks {
users {
id
displayName
ttlocks {
# Remote schema relation in users table
lockName
lockId
details {
state {
state
}
electricQuantity
firmwareRevision
hardwareRevision
}
}
}
}
Supported APIs
APIs can be added upon request. PRs are welcome of course!
https://euopen.ttlock.com/document/doc?urlName=userGuide%2FekeyEn.html
| Api | Supported | Comment | | ----------------- | :-------: | ------- | | User | 🔴 | | | Lock | 🟠 | | | Ekey | 🔴 | | | Passcode | 🟢 | | | Gateway | 🟠 | | | AddressToken | 🔴 | | | IC Card | 🔴 | | | Fingerprint | 🔴 | | | Group | 🔴 | | | Unlock record | 🟠 | | | Lock upgrade | 🔴 | | | Wireless Keyboard | 🟢 | | | Remote | 🔴 | | | Door Sensor | 🔴 | | | NB-IoT | 🔴 | | | QR Code | 🔴 | | | WiFi Lock | 🔴 | |
🟢 - Supported 🟠 - Partial 🔴 - Not supported
Getting Started
Install the package:
pnpm add @dodi-smart/ttlock-graphql-api
Setup serverless function
Create a new Serverless Function: functions/graphql/ttlock.ts
:
import { createTTlockGraphQLServer } from '@dodi-smart/ttlock-graphql-api'
const server = createTTlockGraphQLServer({
graphiql: true,
graphqlEndpoint: '/graphql/ttlock',
healthCheckEndpoint: '/graphql/ttlock/health',
provideAuth(context) {
return {
// Should provide authentication
}
},
onUpdateSession(context, auth) {
// Handle session update
}
})
export default server
Setup TTLock Management
Go to TTLock Management and obtain OAuth2 API key and secret by creating an application and wait for approval.
- Add
TTLOCK_CLIENT_ID
as an environment variable. - Add
TTLOCK_CLIENT_SECRET
as an environment variable.
If you're using Nhost, add TTLOCK_CLIENT_ID
, TTLOCK_CLIENT_SECRET
to .env.development
like this:
TTLOCK_CLIENT_ID=6EYY••••
TTLOCK_CLIENT_SECRET=BPum••••
And add the production key values to environment variables in the Nhost dashboard.
Start Nhost
nhost up
Learn more about the Nhost CLI.
Test
Test the TTLock GraphQL API in the browser:
https://local.functions.nhost.run/v1/graphql/ttlock
Remote Schema
Add the TTLock GraphQL API as a Remote Schema in Hasura.
URL
{{NHOST_FUNCTIONS_URL}}/graphql/ttlock
Headers
x-nhost-webhook-secret: NHOST_WEBHOOK_SECRET (From env var)
The
NHOST_WEBHOOK_SECRET
is used to verify that the request is coming from Nhost. The environment variable is a system environment variable and is always available.
Authentication
You have several options to authentication your users agains TTLock using this library. All of them depends on the usecase:
Single user usecase (easy to setup)
When the system should work with the locks of a single user (administrator). Permissions and access to the locks are granted to others users via the administrator or using Hasura user roles.
Multi user usecase (advanced)
When system work with locks of multiple users. Each user can authenticate against TTLock using their own credentials via OAuth 2.0. This usecase can be used for multi tenant apps for example.
How to choose
| Use case | Using JWT Token (easy) | OAuth 2.0 (advanced) | | ----------- | :--------------------: | :------------------: | | Single user | 🟠 | 🟢 | | Multi user | 🔴 | 🟢 |
🟢 - Preferable way 🟠 - Supported 🔴 - Not supported
Using JWT Token
Obtain a JWT token by logging the user via curl
, paw
or postman
. The Refresh token is valid for 10 years.
- Add
TTLOCK_ACCESS_TOKEN
as an environment variable. - Add
TTLOCK_REFRESH_TOKEN
as an environment variable.
If you're using Nhost, add TTLOCK_ACCESS_TOKEN
and TTLOCK_REFRESH_TOKEN
to .env.development
like this:
TTLOCK_ACCESS_TOKEN=59f6d••••
TTLOCK_REFRESH_TOKEN=BPum••••
And add the production key values to environment variables in the Nhost dashboard.
Configure the TTLock GraphQL API
import { createTTLockGraphQLServer } from '@dodi-smart/ttlock-graphql-api'
const server = createTTLockGraphQLServer({
graphqlEndpoint: '/graphql/ttlock',
healthCheckEndpoint: '/graphql/ttlock/health',
provideAuth(context) {
const { request } = context
return {
accessToken: process.env["TTLOCK_ACCESS_TOKEN"]
refreshToken: process.env["TTLOCK_REFRESH_TOKEN"]
}
},
onUpdateSession(context, auth) {
// Can be empty as TTLOCK_REFRESH_TOKEN is valid for 10 years
}
})
export default server
OAuth 2.0
From the TTLock Management page get client id and secret.
- Add
TTLOCK_CLIENT_ID
as an environment variable. - Add
TTLOCK_CLIENT_SECRET
as an environment variable.
If you're using Nhost, add TTLOCK_CLIENT_ID
and TTLOCK_CLIENT_SECRET
to .env.development
like this:
TTLOCK_CLIENT_ID=59f6d••••
TTLOCK_CLIENT_SECRET=BPum••••
Configure the TTLock GraphQL API
import { createTTLockGraphQLServer } from '@dodi-smart/ttlock-graphql-api'
const server = createTTLockGraphQLServer({
graphqlEndpoint: '/graphql/ttlock',
healthCheckEndpoint: '/graphql/ttlock/health',
provideAuth(context) {
const { userClaims } = context
const { user } = await gqlSDK.getUser({
id: userId
})
return {
// Example if ttlock creds are saved in user's metadata, can differ
accessToken: user.metadata.ttlockAccessToken,
refreshToken: user.metadata.ttlockRefreshToken,
}
},
onUpdateSession(context, auth) {
const { userClaims } = context
const { accessToken, refreshToken } = auth
await gqlSDK.updateUserTTLockAuth({
id: userId,
accessToken,
refreshToken
})
}
})
export default server
Permissions
Here's a minimal example without any custom permissions. Only requests using the x-hasura-admin-secret
header will work:
import { createTTLockGraphQLServer } from '@dodi-smart/ttlock-graphql-api'
const server = createTTLockGraphQLServer({
graphqlEndpoint: '/graphql/ttlock',
healthCheckEndpoint: '/graphql/ttlock/health',
provideAuth(context) {
/* ... */
},
onUpdateSession(context, auth) {
/* ... */
}
})
export default server
For more granular permissions, you can pass an isAllowed
function to the createTTLockGraphQLServer
. The isAllowed
function takes a context
as parameter and runs every time the GraphQL server makes a request to TTLock to get or modify data for a specific TTLock user.
Here is an example of an isAllowed
function:
import { createTTLockGraphQLServer } from '@dodi-smart/ttlock-graphql-api';
const isAllowed = (context: Context) => {
const { isAdmin, userClaims } = context
// allow all requests if they have a valid `x-hasura-admin-secret`
if (isAdmin) {
return true
}
// get user id
const userId = userClaims['x-hasura-user-id']
// check if the user is signed in
if (!userId) {
return false
}
// get more user information from the database
const { user } = await gqlSDK.getUser({
id: userId
})
if (!user) {
return false
}
// other checks
}
const server = createTTLockGraphQLServer({
graphqlEndpoint: '/graphql/ttlock',
healthCheckEndpoint: '/graphql/ttlock/health',
isAllowed,
provideAuth(context) {
/* ... */
},
onUpdateSession(context, auth) {
/* ... */
}
});
export default server;
Context
The context
object contains:
userClaims
- verified JWT claims from the user's access token.isAdmin
-true
if the request was made using a validx-hasura-admin-secret
header.request
- Fetch API Request object that represents the incoming HTTP request in platform-independent way. It can be useful for accessing headers to authenticate a userquery
- the DocumentNode that was parsed from the GraphQL query stringoperationName
- the operation name selected from the incoming queryvariables
- the variables that were defined in the queryextensions
- the extensions that were received from the client
Read more about the default context from GraphQL Yoga.