RDS Supabase SMS Authentication Configuration (SMS Webhook + Alibaba Cloud SMS Provider)

Updated at:

Learn how to configure and enable SMS authentication for your RDS Supabase application. This allows users to register and log in quickly using only a phone number and a one-time password (OTP). This feature uses Alibaba Cloud Short Message Service (SMS) to send OTPs. You can integrate it in two ways: by building a custom SMS Webhook with Function Compute to connect to any external SMS gateway, or by using the built-in Alibaba Cloud SMS Provider for direct configuration.

Background information

RDS Supabase provides an authentication system (Auth) that supports multiple sign-in methods. SMS authentication is a key option. It allows users to register and log in using only a phone number and an OTP. This eliminates the need for complex passwords to set or remember. This improves both user experience and account security.

To send SMS messages, RDS Supabase supports two configuration options:

  • SMS Webhook (recommended): You provide a public HTTPS endpoint (a Hook) that acts as a custom SMS gateway. This method is more flexible. It lets you connect to any third-party SMS service or your own SMS gateway. It is also not limited by the constraints of the underlying SMS service. Use it when you need custom signatures and templates or other custom behavior.

  • Alibaba Cloud SMS Provider: You configure Alibaba Cloud SMS credentials directly in RDS Supabase. This method is simple and integrates seamlessly with the Alibaba Cloud ecosystem. However, it depends on the underlying SMS service, and some custom signatures and templates may not be compatible.

This topic explains how to configure both options.

Prerequisites

  • You have created an active Alibaba Cloud RDS Supabase instance.

  • You have activated Alibaba Cloud Short Message Service.

  • You have applied for and obtained an approved SMS signature and template in the Short Message Service console.

    Important
    • When you use the Alibaba Cloud SMS Provider method, use the system-provided signature and template from Alibaba Cloud Short Message Service. Custom signatures and templates may not be compatible because of constraints in the underlying SMS service. If you need custom signatures and templates, use the SMS Webhook method instead.

    • You do not need enterprise credentials for testing. You can use the free, pre-approved SMS authentication product offered by Alibaba Cloud. In this example, the system-provided signature is SpeedLink Verification Code, and the system-provided template code is 100001.

  • (Optional, for SMS Webhook only) You have activated Alibaba Cloud Function Compute (FC) to deploy your SMS-sending Hook function.

Procedure

The full configuration process has three parts: configuring RDS Supabase authentication parameters, configuring network settings, and verifying that the feature works. Complete the steps for your chosen SMS delivery method.

Configure RDS Supabase authentication parameters

Method 1: Use an SMS Webhook (recommended)

This method uses your own Hook function to send SMS messages, offering more flexibility. This example uses Alibaba Cloud Function Compute (FC) to host the Hook function.

Step 1: Create and deploy the SMS-sending function

This step creates an HTTP-triggered function that acts as RDS Supabase's SMS Webhook.

  1. Prepare the function code. Create a project folder with the following standard structure: package.json (lists dependencies), tsconfig.json (TypeScript configuration), .env (environment variables), and the core logic file src/server.ts. You can download the full project from the attached archive code.zip.

    The core logic is in src/server.ts. This file receives webhook requests from Supabase, validates an optional key, and calls the Alibaba Cloud SMS API.

    server.ts

    import express, { Request, Response } from 'express';
    import dotenv from 'dotenv';
    import { Webhook } from 'standardwebhooks';
    dotenv.config();
    
    // Alibaba Cloud SDK
    import Dypnsapi20170525, { SendSmsVerifyCodeRequest } from '@alicloud/dypnsapi20170525';
    import { Config as OpenApiConfig } from '@alicloud/openapi-client';
    import * as Util from '@alicloud/tea-util';
    
    
    const app = express();
    app.use(express.json());
    
    // Read config from environment variables
    const ACCESS_KEY_ID = process.env.ALICLOUD_ACCESS_KEY_ID!;
    const ACCESS_KEY_SECRET = process.env.ALICLOUD_ACCESS_KEY_SECRET!;
    const REGION_ID = process.env.REGION_ID || 'ap-southeast-1';
    const SIGN_NAME = process.env.SIGN_NAME!;
    const TEMPLATE_CODE = process.env.TEMPLATE_CODE!;
    const PORT = parseInt(process.env.PORT || '3000');
    const SMS_HOOK_SECRET = process.env.SMS_HOOK_SECRET;
    
    // Create DYPNS client
    function createClient(): Dypnsapi20170525 {
      const config = new OpenApiConfig({
        accessKeyId: ACCESS_KEY_ID,
        accessKeySecret: ACCESS_KEY_SECRET,
        regionId: REGION_ID,
        endpoint: 'dypnsapi.aliyuncs.com',
        protocol: 'https'
      });
      return new Dypnsapi20170525(config);
    }
    
    const client = createClient();
    /**
     * Verify Supabase webhook request
     * @param req - Express request object
     * @throws Error if verification fails
     */
    export function verifySupabaseWebhook(req: Request): void {
      const SMS_HOOK_SECRET = process.env.SMS_HOOK_SECRET;
      
      // Skip verification if SMS_HOOK_SECRET is not set
      if (!SMS_HOOK_SECRET) {
        console.warn('SMS_HOOK_SECRET not configured, skipping webhook verification');
        return;
      }
      
      try {
        // Use standardwebhooks library to verify
        const secret = SMS_HOOK_SECRET.replace('v1,whsec_', '');
        const wh = new Webhook(secret);
        
        // Get raw request body
        const payload = JSON.stringify(req.body);
        
        // Use req.headers directly (Express.js gives key-value pairs)
        const headers = req.headers as Record<string, string>;
        
        // Verify webhook
        wh.verify(payload, headers);
      } catch (error: any) {
        if (error.message.includes('missing')) {
          throw new Error('Missing required webhook headers');
        } else if (error.message.includes('signature')) {
          throw new Error('Invalid webhook signature');
        } else if (error.message.includes('timestamp')) {
          throw new Error('Webhook timestamp is too old or in the future');
        }
        throw new Error('Webhook verification failed');
      }
    }
    
    // Supabase SMS Hook endpoint: receive request and send SMS
    app.post('/hooks/send-sms', async (req: Request, res: Response) => {
      console.log('=== Received Supabase SMS Hook request ===');
      console.log('Request time:', new Date().toISOString());
      console.log('Request headers:', JSON.stringify(req.headers, null, 2));
      console.log('Request body:', JSON.stringify(req.body, null, 2));
      
      try {
        // Verify request
        verifySupabaseWebhook(req);
        console.log('JWT verification passed');
      } catch (error: any) {
        console.error('JWT verification failed:', error.message);
        return res.status(401).json({ 
          error: error.message || 'Unauthorized',
          code: 'UNAUTHORIZED'
        });
      }
    
      const { user, sms } = req.body;
      console.log('Parsed user info:', user);
      console.log('Parsed SMS info:', sms);
      
      // Validate required fields
      if (!user || !user.phone || !sms || !sms.otp) {
        console.error('Missing required fields:', { user, sms });
        return res.status(400).json({ error: 'Missing required fields: user.phone or sms.otp' });
      }
      
      const phone = user.phone;
      const otp = sms.otp;
      console.log('Phone number:', phone);
      console.log('OTP:', otp);
    
      // Extract digits only (remove prefixes like +86)
      const phoneNumber = phone.replace(/\D/g, '').slice(-11);
      console.log('Processed phone number:', phoneNumber);
    
      // Build template parameters. Default expiry is 5 minutes. Must match GOTRUE_SMS_OTP_EXP in Supabase.
      const templateParam = JSON.stringify({
        code: otp,
        min: '5'
      });
      console.log('SMS template parameters:', templateParam);
    
      try {
        const sendReq = new SendSmsVerifyCodeRequest({
          signName: SIGN_NAME,
          templateCode: TEMPLATE_CODE,
          phoneNumber: phoneNumber,
          templateParam: templateParam,
        });
        
        console.log('Preparing SMS request:', JSON.stringify(sendReq, null, 2));
    
        const runtime = new Util.RuntimeOptions({});
        const response = await client.sendSmsVerifyCodeWithOptions(sendReq, runtime);
        console.log('SMS service response:', JSON.stringify(response, null, 2));
          
        // Check success
        if (response.statusCode === 200 && response.body && response.body.success === true) {
          // Fix: Return JSON format expected by Supabase
          console.log('SMS sent successfully');
          return res.status(200).json({ success: true });
        } else {
          console.error('SMS failed:', {
            statusCode: response.statusCode,
            apiCode: response.body?.code,
            apiMessage: response.body?.message,
            apiSuccess: response.body?.success
          });
          return res.status(500).json({ 
            error: 'Failed to send SMS', 
            statusCode: response.statusCode,
            apiCode: response.body?.code,
            apiMessage: response.body?.message,
            apiSuccess: response.body?.success
          });
        }
      } catch (err: any) {
        console.error('Exception while sending SMS:', err);
        return res.status(500).json({
          error: err.message || 'Internal error',
          recommend: err.data?.Recommend,
        });
      }
    });

Step 2: Deploy to Function Compute. This step packages and deploys the code to Alibaba Cloud Function Compute.

  1. Download and extract the project code to your local machine. In the .env file, set your Alibaba Cloud AccessKey, AccessSecret, SMS signature, and template.

    Note

    On macOS and Linux, the .env file may be hidden by default.

  2. After updating the four files, package them. Then, run these commands:

    npm install
    npm run build
    zip -rq -y code.zip ./
  3. Log on to the Function Compute console. Select the same region as your RDS Supabase instance, then click Create Function.

  4. Select Web Function. Set the runtime environment to Node.js 20, then upload the code.zip package.

  5. In Advanced Configuration, set the start command to npm start and the listening port to 80.

  6. After creation, go to the function's details page. On the Triggers tab, find the public endpoint URL. Then, disable authentication for this trigger.

  7. Record the public endpoint URL, which looks like https://xxx.ap-southeast-1.fcapp.run. You will use this as the Webhook URI in the next step.image

  8. (Optional) Test the Webhook endpoint. After deployment, test the function locally with curl. Replace phone with a test phone number that you registered in the Alibaba Cloud Short Message Service console.

    curl -X POST https://xxx.ap-southeast-1.fcapp.run/hooks/send-sms \
      -H "Content-Type: application/json" \
      -d '{
        "user": { "phone": "+861xxxxxxxxxx" },
        "sms": { "otp": "123456" }
      }'
    

    If configured correctly, the test phone will receive an OTP SMS.

Step 3: Configure the RDS Supabase instance

This step adds the Webhook details to your RDS Supabase instance.

  1. Go to the details page of your RDS Supabase instance. Choose Auth Configuration > Phone Number > SMS Webhook.

  2. Configure the following parameters:

    • SMS Sending: Set to true.

    • Enter an HTTPS URL for confirmation SMS: Enter the public endpoint URL that you obtained in Step 2, plus the path /hooks/send-sms. For example: https://xxx.fcapp.run/hooks/send-sms.

    • Hook Key: (Optional but recommended) Add a key to improve security. The format should start with v1,whsec_, followed by a 60-byte Base64-encoded string. If the format is incorrect, the backend generates a correct secret. This value must match the SMS_HOOK_SECRET environment variable in Function Compute.

      Hook key generation script

      # Generate 60 random bytes and encode as Base64
      SECRET=$(openssl rand -base64 60 | tr -d '\n')
      # Add prefix
      echo "v1,whsec_$SECRET"
    • SMS Auto Confirm: Set this option to false.

    • SMS OTP Time-to-Live (TTL) (seconds): Set how long the OTP remains valid, in seconds. The default value is 60.

  3. Configure Function Compute environment variables (optional but recommended). To ensure that only your RDS Supabase instance calls this Webhook, you must sync the Webhook key to Function Compute if you have configured one. In the Function Compute console, find your function. In the Environment Variables section, add a variable named SMS_HOOK_SECRET. Its value must exactly match the Hook Key from the previous step. With this shared key, the function checks the x-supabase-webhook-signature header on every request and rejects unauthorized requests.

  4. Submit the parameters and wait for the instance to restart.

Method 2: Use the Alibaba Cloud SMS Provider

This method integrates RDS Supabase directly with Alibaba Cloud SMS.

Step 1: Prepare Alibaba Cloud access credentials (AccessKey)

To allow RDS Supabase to call the SMS API, create a Resource Access Management (RAM) user with the required permissions and obtain its AccessKey. Log on to the Alibaba Cloud website. You can use an existing AccessKey or follow Create an AccessKey to create a new one.

Important
  • Use a RAM user's AccessKey instead of your root account's. This reduces the security risk if the key is exposed.

  • For security, Alibaba Cloud shows the AccessKey secret only once, at creation time. You cannot view it later, so store it securely.

Step 2: Configure RDS Supabase authentication parameters

  1. Log on to the RDS console. In the navigation pane on the left, click AI Application Development.

  2. Select your region at the top of the page. Then, click the ID of your target instance to open its details page.

  3. In the navigation pane on the left, click Auth Configuration.

  4. On the Authentication Provider page, configure the following parameters:

    • Access Key ID: Enter the RAM user's Access Key ID that you obtained in Step 1.

    • Access Key Secret: Enter the RAM user's Access Key Secret that you obtained in Step 1.

    • SMS Region: See service endpoints for supported Alibaba Cloud SMS regions, such as ap-southeast-1.

    • SMS Signature Name: Enter the signature name that you applied for in the Alibaba Cloud Short Message Service console, such as SpeedLink Verification Code.

    • SMS Template Code: Enter the template code that you applied for in the Alibaba Cloud Short Message Service console, such as 100001.

    • Use system-provided SMS signature and template: Enable this option if you are using the test signature and template provided by Alibaba Cloud Short Message Service. Disable it if you are using your own signature and template. Custom signatures and templates may not be compatible because of constraints in the underlying SMS service. For reliable delivery, use the system-provided signature and template with this option enabled.

    • SMS OTP Time-to-Live (TTL) (seconds): Set how long the OTP remains valid, in seconds. The default value is 60.

    • SMS Auto Confirm: Keep this option disabled. If enabled, users who register with a phone number become confirmed users immediately, which is not recommended.

  5. After you finish, click Confirm. Your instance will restart automatically to apply the changes. Wait for the restart to complete.

Note
  • When both SMS Webhook and Alibaba Cloud SMS Provider are configured, SMS Webhook is used because SMS Webhook has a higher priority.

General network configuration

Perform these steps for both methods.

Step 4: Configure the RDS Supabase whitelist

To allow your frontend app to access your RDS Supabase instance, add its public IP address to the whitelist.

  1. On the instance details page, in the Basic Information section, locate Whitelist Information.

  2. Click Create Whitelist. Then, add the IP address of your client or test server.

Step 5: Enable public network access for the instance

To allow your RDS Supabase instance to call external APIs, such as the SMS gateway or your Hook function, enable public network access.

  1. On the instance details page, go to Basic Information > Network Information. Turn on Attach EIP. This allows RDS Supabase to call your Function Compute Webhook or the Alibaba Cloud SMS API.

Feature verification

After you complete all configurations, build a simple frontend project to test the SMS login flow.

  1. Verification code. Download and extract the frontend verification project to your local machine. The project includes index.html (UI), server.ts (Supabase client configuration), and app.ts (core authentication logic). You can obtain the full runnable project from the attached archive supabase_sms.zip.

    Replace the values for SUPABASE_URL and SUPABASE_ANON_KEY in the .env file. You can find these values after you set the whitelist: click the Outside the network connection address for your RDS Supabase instance, then click Connect in RDS Supabase Studio.

  2. Run and test

    1. In the project root directory, run the following commands to start the service:

      npm install
      npm run dev  // Start development server
      or npm start 
    2. Open http://localhost:3000 in your browser.

    3. Enter a phone number and click Send OTP. Verify that you receive the OTP on your phone.

    4. Enter the OTP and click Verify. The page should display a success message and user details.

    5. Log on to RDS Supabase Studio. Go to Authentication > Users. You should see the new user record.

Cost and risk summary

  • Cost breakdown: This solution uses these billable services:

    • RDS Supabase: Instance type fees.

    • Function Compute: (Webhook method only) Billed per resource usage, invocation count, and outbound Internet traffic. Includes a free quota.

    • Short Message Service: Billed per message sent.

  • Key risks:

    • Credential exposure: If an AccessKey leaks, SMS resources could be abused. Use a RAM user with minimal required permissions and store credentials securely.

    • Webhook security: (Webhook method only) A public Webhook endpoint could be called by attackers. You can configure a Hook Key and validate it in your function to confirm the request source.