Reverse proxy for guest user access?

I’ve got this use case where I want to be able to give guests or employees limited access. Things like to open the garage door to the shop…

I’ve read a bunch of threads on guest access and not found anything quite like I want. Ideally the guest can access a URL and they get access to one specific dashboard. For employees I’d like the same, but ideally keep some track of who did what and when.

I’ve been using kiosk mode to secure my phones dashboard when it’s locked (android device control), but that works because you can’t modify the URL path to disable kiosk mode.

Tonight, I wondered if I could create a dashboard and use a reverse proxy to access just that dash, that might keep people from being able to append queries that break out of kiosk mode right? From a security/access control perspective I could do authentication on the reverse proxy. A downside to this is that HA would see everyone as one user, so there’s no logging about who did what if I had multiple users.

Just spit balling here, could that work and be “secure”? Any other ideas?

I create JavaScript webpage and linked it to HA API with auth token.

Page has “doorbell” and “open with code” button.

I created OTP sensor for each user. They can enter OTP to allow opening. This prevent sharing and requires time of use authorization. It also allows me to see who entered code to open

There is automation that verifies OTP code along with any other rules, like time of day limits, and executes appropriate action. In this case it either opens gate, sends image to me and rings bell or if wrong code it rings the bell and send notification.

Since the page only sends info to HA (the OTP code) there is no possibility of sidestepping this to open

If interested I can share later

1 Like

This sounds like a really solid solution.

I don’t fully understand where the token will be that it’s effective, but can’t be reverse engineered, however I know very little about JavaScript.

If you’d be willing to share, I think I would benefit, as well as some others who’ve looked for something similar.

Thank you!

Site send http post with OTP as data. This gets redirected by my proxy server to weblink with bearer token to send data to HA automation. I need to check if it is possible to retrieve the redirect link which would also mean grabbing bearer token but for now I’m not terribly concerned and actually more concerned about someone with access to the webpage spamming the doorbell

I will edit my first post with code. Worst case maybe someone can improve it. I’m no coder but can make useful junk every once in a while :grin:

I’m following you know, at least in concept. I’m happy to help trying to exploit it too. :grin:

In my case it’s only going to be exposed via secure WiFi, and highly unlikely any person connecting will have the background to realize and exploit a vulnerability, but… I’d rather prefer to keep it properly locked down.

1 Like

HA AUTOMATION

alias: ACTION_OTPWebhookGateOpen
description: ""
trigger:
  - platform: webhook
    allowed_methods:
      - POST
      - PUT
      - GET
      - HEAD
    local_only: false
    webhook_id: webhook2
    id: code
  - platform: webhook
    allowed_methods:
      - POST
      - PUT
      - GET
      - HEAD
    local_only: false
    webhook_id: webhook1
    id: doorbell
condition: []
action:
  - if:
      - condition: trigger
        id:
          - code
      - condition: template
        value_template: "{{ (trigger.json) == (states.sensor.person_otp.state) }}"
        enabled: true
    then:
      - service: switch.turn_on
        target:
          entity_id: switch.gate_switch_3
        data: {}
        enabled: true
      - service: notify.mobile_app_myphone
        data:
          data:
            entity_id: camera.frigate_gate03
          message: The Gate was opened using Code
    else:
      - if:
          - condition: state
            entity_id: siren.doorbell_play_tone
            state: "off"
            for:
              hours: 0
              minutes: 0
              seconds: 10
            enabled: true
mode: single

EDIT

It’s not in this code but the else condition needs a then action. Should be simple enough to add. I deleted by mistake when sanitizing for posting here

1 Like

NGINX PROXY

    #############################################
    #                 HTTPS MYSITE         #
    #############################################
    server {
        listen               443 ssl;
        http2 on;
        server_name         mysite.com;
        access_log           /var/log/nginx/access/mysite.log;
        error_log            /var/log/nginx/error/mysite.log;
        ssl_certificate      /etc/cloudflare/mysite.pem;
        ssl_certificate_key  /etc/cloudflare/mysite.key;


        ssl_protocols        TLSv1.3 TLSv1.2;
        ssl_ciphers          HIGH:!aNULL:!MD5;
        ssl_stapling          on;
        ssl_stapling_verify   on;
        ssl_session_timeout  5m;

        location / {
            root /var/www/mysite;
        }


        location /doorbell {
            return 308 https://mysite.com/api/webhook/webhook1;
        }

        location /gatecode {
            return 308 https://mysite.com/api/webhook/webhook2;
        }



    } #end mysite


webhooks are created at https://yourHA.com/config/cloud/account

1 Like

HTML SITE

<!DOCTYPE html>
<html lang="en">
<body>


<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Welcome to MY Home </title>


<style>
body {
  color: #FFFFFF;
  text-shadow: 10px 10px 10px #000000;
  background-image: url('photo.jpg');
  width: 100%;
  min-height: 100vh;
  background-repeat: no-repeat;
  background-position: 75% center;
  background-attachment: fixed;
  -webkit-background-size: cover;
  -moz-background-size: cover;
  -o-background-size: cover;
  background-size: cover;
}


.btn {
  background: #3498db;
  background-image: -webkit-linear-gradient(top, #3498db, #2980b9);
  background-image: -moz-linear-gradient(top, #3498db, #2980b9);
  background-image: -ms-linear-gradient(top, #3498db, #2980b9);
  background-image: -o-linear-gradient(top, #3498db, #2980b9);
  background-image: linear-gradient(to bottom, #3498db, #2980b9);
  -webkit-border-radius: 33;
  -moz-border-radius: 33;
  border-radius: 33px;
  -webkit-box-shadow: 4px 5px 3px #697cdb;
  -moz-box-shadow: 4px 5px 3px #697cdb;
  box-shadow: 4px 5px 3px #697cdb;
  font-family: Arial;
  color: #f5ebf5;
  font-size: 20px;
  padding: 10px 20px 10px 20px;
  text-decoration: none;
  margin: 12px;
}

.btn:hover {
  background: #29dde3;
  background-image: -webkit-linear-gradient(top, #29dde3, #3498db);
  background-image: -moz-linear-gradient(top, #29dde3, #3498db);
  background-image: -ms-linear-gradient(top, #29dde3, #3498db);
  background-image: -o-linear-gradient(top, #29dde3, #3498db);
  background-image: linear-gradient(to bottom, #29dde3, #3498db);
  text-decoration: none;
}
</style>


</head>


<input type="text" id="currentDateTime">
<h1>CMy House</h1>
<h2>My Address</h2>



  <button class="btn" onclick="Doorbell()">Ring Doorbell</button> 
  <button class="btn" onclick="OpenGate()">Open Gate</button>


  <script>
    function Doorbell() {
      let phonenum = prompt("Please Enter Your Phone Number", "");
      var request = new XMLHttpRequest();
      request.open("POST", "https://mysite.com/doorbell");

      request.setRequestHeader('Content-type', 'application/json');

      request.send(JSON.stringify(phonenum));
    }
  </script>

  <script>
    function OpenGate() {
      let code = prompt("Enter Code", "");
      
      if (code != null) {
      var request = new XMLHttpRequest();
      request.open("POST", "https://mysite.com/gatecode");

      request.setRequestHeader('Content-type', 'application/json');

      request.send(JSON.stringify(code));
      }
    }
  </script>

  <script>
    var today = new Date();
    var date = today.getFullYear()+'-'+(today.getMonth()+1)+'-'+today.getDate();
    var time = today.getHours() + ":" + today.getMinutes() + ":" + today.getSeconds();
    var dateTime = date+' '+time;
    document.getElementById("currentDateTime").value = dateTime;
  </script>	

</html>
1 Like

It’s gonna take me a bit to implement and test this. I’ll post back when I get it set up. :call_me_hand:t3: Thank you!