DNSDig API
As mentioned before, DNSDig API is built on top of FastAPI, Pydantic and DNSPython. Authentication and authorization is handled by Kinde. MongoDB is the choice for the database and Redis is used for rate limiting so far.
This started as a hack and challenge to finish in 24 hours but I can say that it's still a work in progress. The API is deployed at https://dnsdig-api.bango29.com and the endpoint docs are available at https://dnsdig-api.bango29.com/docs. The API is free to use but rate limited as described further down in this page.
Register for an account and create an application if you want to use the API without rate limits.
Authentication & Authorization
Kinde is responsible to issue access tokens, refresh tokens and revoke tokens. DNSDig API will only accept access tokens issued by Kinde. The access token is passed in the Authorization
header with the Bearer
scheme. The access tokens are issued as JWT tokens.
User Flow
DNSDig API uses OAuth 2.0 to authenticate users indirectly by using Kinde. The followings are sequence diagrams of how the authentication flow works. The intended audience for this flow are humans using a web browser.
Login/Register Flow
sequenceDiagram
autonumber
User -> "Web Frontend": Clicks the login or register button
"Web Frontend" ->> "DNSDig API": [GET] /v1/me/login-url
"DNSDig API" ->> "Web Frontend": Responds with a login URL
"Web Frontend" ->> User: Redirects to the login URL
User ->> Kinde: Logs in or registers
Kinde ->> "Web Frontend": [GET] /callbacks/kinde
"Web Frontend" ->> "DNSDig API": [GET] /v1/callbacks/kinde
"DNSDig API" ->> Kinde: Exchange the code for an access token
Kinde ->> "DNSDig API": Respond with the access token
"DNSDig API" ->> "DNSDig API": Maybe register a new user
"DNSDig API" ->> "Web Frontend": Responds with the access token and refresh token
"Web Frontend" ->> User: Redirects to homepage
Refresh Token Flow
sequenceDiagram
autonumber
User ->> "Web Frontend": Resolve a hostname
"Web Frontend" ->> "DNSDig API": [GET] /v1/resolve/google.com
"DNSDig API" ->> "Web Frontend": Responds with a 401 - Access Token is expired
"Web Frontend" ->> "DNSDig API": [GET] /v1/oauth2/token
"DNSDig API" ->> Kinde: Exchange refresh token for an access token
Kinde ->> "DNSDig API": Respond with an access token
"DNSDig API" ->> "Web Frontend": Responds with the access token and refresh token
"Web Frontend" ->> "DNSDig API": [GET] /v1/resolve/google.com
"DNSDig API" ->> "Web Frontend": Responds with the resolved records
"Web Frontend" ->> User: Renders the resolved records page
Machine to Machine Flow
Before being able to make requests to the API, a Machine to Machine (M2M) application needs to register their application first through DNSDig API's web frontend. This flow as the name implies is intended for machine to machine communication. It's not recommended to use this flow for mobile apps.
This part of the API does not use Kinde for authentication and authorization. Instead it's a custom access token and refresh token issuance that looks like an OAuth 2.0 flow. The access token is not a JWT token, it's a random string prefixed with m2m
.
Register Application Flow
sequenceDiagram
autonumber
Developer ->> "Web Frontend": Registers a new application
"Web Frontend" ->> "DNSDig API": [POST] /v1/me/applications
"DNSDig API" ->> "Web Frontend": Responds with client credentials
"Web Frontend" ->> Developer: Render application detail page
Get My Applications Flow
sequenceDiagram
autonumber
Developer ->> "Web Frontend": Clicks on "My Applications"
"Web Frontend" ->> "DNSDig API": [GET] /v1/me/applications
"DNSDig API" ->> "Web Frontend": Responds with this developer's applications
"Web Frontend" ->> Developer: Render application list page
Obtain Access Token
sequenceDiagram
autonumber
Machine ->> "DNSDig API": [POST] /v1/oauth2/token
note right of Machine: Client ID and Client Secret in the request body with a grant type of client credentials
"DNSDig API" ->> MongoDB: Save new access token and refresh token pair
MongoDB ->> "DNSDig API": Respond with success
"DNSDig API" ->> Machine: Responds with the access token and refresh token
Refresh Token
sequenceDiagram
autonumber
Machine ->> "DNSDig API": [POST] /v1/oauth2/token
note right of Machine: Refresh token included in the request body with a grant type of refresh token
"DNSDig API" ->> MongoDB: Save new access token and refresh token pair
MongoDB ->> "DNSDig API": Respond with success
"DNSDig API" ->> Machine: Responds with the access token and refresh token
Rate Limits
In DNSDig, access tokens are used for certain endpoints to avoid rate limiting. Requests that are authorized with access tokens will have an unlimited rate limit from the API's perspective than requests that are not authorized with access tokens. Rate limiting authorized requests is delegated to a reverse proxy like nginx or Traefik. DNSDig's publicly reachable API is deployed behind Cloudflare, therefore Cloudflare's policy applies.
Rate Limited Endpoints
The endpoints' rate limit is configurable via these environment variables:
Name | Description |
---|---|
THROTTLER_TIMES |
Required string |
THROTTLER_SECONDS |
Required string |
THROTTLER_SECONDS
determines the number of seconds elapsed before the rate limit is reset. THROTTLER_TIMES
determines the number of requests allowed to be made within THROTTLER_SECONDS
seconds.
Implementation
Rate limiting is implemented by using the fastapi-limiter library. In practice Redis is required to maintain the state of the rate limit. The Redis connection is configured via the REDIS_URL
environment variable. Below is taken from a public endpoint protected with the rate limiter. The settings
variable is a Pydantic BaseSettings
class that is used to read environment variables and or .env
files.
Raise Exceptions Anywhere
In the example endpoint written above, the mechanics of the endpoint is wrapped with an async context manager to ensure MongoDB's transactions are in effect. Therefore, whenever an exception is raised anywhere in the codebase (even by 3rd party codes in libraries), the transaction will then be rolled back, no changes are saved to MongoDB. This is particularly useful to avoid half measured database operations.
Anywhere in the codebase, it's encouraged to raise exceptions instead of returning error responses. The exception will be caught by the exception handler and then the error response will be returned to the client. Managed exceptions are raised using FastAPI's standard exception object HTTPException
.
IPv6 Support
DNSDig API is built on top of dnspython in which IPv6 resolvers are supported. However, depending on your own network setup, DNSDig API might not be able to resolve records using IPv6 resolvers. For this reason, IPv6 endpoints are dedicated and ends with a 6
prefix. If your network supports IPv6 and a request is made to one of the IPv6 endpoints, the API will use IPv6 resolvers to resolve the records.
On the contrary, AAAA
records can be resolved regardless if you use the IPv6 endpoints or the regular endpoints.
DNSDig API also have tests for IPv6 resolvers but as of now commented from the source code. This is because Github's workflow runners does not support IPv6. If this changes then the tests will be uncommented.
IP Geolocation
DNSDig API uses ipinfo.io to geolocate IP addresses. The API is rate limited to 50,000 requests per month. The API is free to use but you need to register for an account to get an API key. When this quota is used up, DNSDig API will not include geolocation data in the response.