Skip to content

Commit

Permalink
Support a new "secret" auth method on the phone
Browse files Browse the repository at this point in the history
This is a partial fix for issue
e-mission/e-mission-docs#628

The new method assumes that all tokens are prefixed by a shared secret between
the server and the client. This means that all tokens have two parts both of which are validated by the method.
- The second part is used to identify the user, randomly generating it on the
  phone ensures that users can only see their own data.
- The first shared part is configured into both the app and the server. It
  ensures that users can only connect to the server from authorized clients,
  which in this case, are smartphone apps that embed the secret.

+ bonus fix to generate the correct error (403 instead of 401) in case of auth errors

Testing done:
- configured the webserver to use the secret method
- configured one valid secret `"FOOBAR"`
    - auth failed on the server
    ```
    END 2021-03-16 11:33:43.257294 POST /result/metrics/timestamp  0.00736689567565918
    Traceback (most recent call last):
      File "emission/net/api/cfc_webapp.py", line 594, in getUUID
        retUUID = enaa.getUUID(request, auth_method, inHeader)
      File "/Users/kshankar/e-mission/e-mission-server/emission/net/auth/auth.py", line 67, in getUUID
        retUUID = getUUIDFromToken(authMethod, userToken)
      File "/Users/kshankar/e-mission/e-mission-server/emission/net/auth/auth.py", line 41, in getUUIDFromToken
        userEmail = AuthMethodFactory.getAuthMethod(authMethod).verifyUserToken(token)
      File "/Users/kshankar/e-mission/e-mission-server/emission/net/auth/secret.py", line 29, in verifyUserToken
        (token, len(self.client_secret_list)))
    ValueError: Invalid token REPLACEMEkVVdF9rT, not found in list of length 1
    ```
    - reflected on the client
    ```
    2021-03-16 11:32:58.087 23394-23394/edu.berkeley.eecs.emission.devapp I/chromium: [INFO:CONSOLE(145)] "Error loading user data"While pushing/getting from server HTTP/1.1 403 Forbidden"", source: http://localhost/_app_file_/data/user/0/edu.berkeley.eecs.emission.devapp/files/phonegapdevapp/www/index.html (145)
    2021-03-16 11:32:58.087 23394-23394/edu.berkeley.eecs.emission.devapp I/chromium: [INFO:CONSOLE(145)] "ERROR:Error loading user data"While pushing/getting from server HTTP/1.1 403 Forbidden"", source: http://localhost/_app_file_/data/user/0/edu.berkeley.eecs.emission.devapp/files/phonegapdevapp/www/index.html (145)
    ```

- configured the same valid secret as the phone `"REPLACEME"`
    e-mission/e-mission-phone@c00f63b
    - auth succeded on server
    ```
    START 2021-03-16 11:36:22.818484 POST /result/metrics/timestamp
    END 2021-03-16 11:36:22.832453 POST /result/metrics/timestamp cf8ccb7b-84d7-40e4-a726-7691e614b042 0.013911962509155273
    ```
    - reflected on client
    ```
    2021-03-16 11:36:21.821 23394-23539/edu.berkeley.eecs.emission.devapp D/ConnectionSettings: in getConnectURL, connectionSettings = {"connectUrl":"http:\/\/10.0.2.2:8080","android":{"auth":{"method":"prompted-auth","clientID":"ignored"}},"ios":{"auth":{"method":"prompted-auth","clientID":"ignored"}}}
    2021-03-16 11:36:21.832 23394-23539/edu.berkeley.eecs.emission.devapp D/ConnectionSettings: in getConnectURL, returning http://10.0.2.2:8080
    2021-03-16 11:36:21.836 23394-9405/edu.berkeley.eecs.emission.devapp I/System.out: Posting data to http://10.0.2.2:8080/result/metrics/timestamp
    2021-03-16 11:36:21.836 23394-9405/edu.berkeley.eecs.emission.devapp I/System.out: About to execute query SELECT data FROM userCache WHERE key = 'prompted-auth' AND type = 'local-storage' AND write_ts >= 0.0 AND write_ts <= 1.615919781836E12 ORDER BY write_ts DESC
    2021-03-16 11:36:21.848 23394-9405/edu.berkeley.eecs.emission.devapp I/PromptedAuth: Auth found in local storage, now it should be stable
    2021-03-16 11:36:21.887 23394-9405/edu.berkeley.eecs.emission.devapp I/CommunicationHelper: Got response org.apache.http.message.BasicHttpResponse@c89c5d6 with status HTTP/1.1 200 OK
    ```
  • Loading branch information
shankari committed Mar 17, 2021
1 parent b5b53df commit 460b9e4
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 1 deletion.
5 changes: 5 additions & 0 deletions conf/net/auth/secret_list.json.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"client_secret_list": [
"FOOBAR"
]
}
4 changes: 3 additions & 1 deletion emission/net/api/cfc_webapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ def getUUID(request, inHeader=False):
return retUUID
except ValueError as e:
traceback.print_exc()
abort(401, e.message)
abort(403, e)

# Auth helpers END

Expand All @@ -515,6 +515,8 @@ def getUUID(request, inHeader=False):
except:
webserver_log_config = json.load(open("conf/log/webserver.conf.sample", "r"))

print(f"Using auth method {auth_method}")

logging.config.dictConfig(webserver_log_config)
logging.debug("This should go to the log file")

Expand Down
4 changes: 4 additions & 0 deletions emission/net/auth/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ def getAuthMethod(methodName):
import emission.net.auth.skip as enas
logging.debug("methodName = skip, returning %s" % enas.SkipMethod)
return enas.SkipMethod()
if methodName == "secret":
import emission.net.auth.secret as enar
logging.debug("methodName = secret, returning %s" % enar.SecretMethod)
return enar.SecretMethod()
elif methodName == "openid_auth":
import emission.net.auth.openid_auth as enao
logging.debug("methodName = openid_auth, returning %s" % enao.OpenIDAuthMethod)
Expand Down
29 changes: 29 additions & 0 deletions emission/net/auth/secret.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import logging
import json
import traceback

class SecretMethod(object):
def __init__(self):
key_file = open('conf/net/auth/secret_list.json')
key_data = json.load(key_file)
key_file.close()
self.client_secret_list = key_data["client_secret_list"]

def verifyUserToken(self, token):
# attempt to validate token on the client-side
logging.debug("Using the SecretAuthMethod to verify id token %s of length %d against secret list %s..." %
(token, len(token), self.client_secret_list[0:10]))
# matching_list = [token == curr_token for curr_token in self.token_list]
# print matching_list
# stripped_matching_list = [token == curr_token.strip() for curr_token in self.token_list]
# print stripped_matching_list
for secret in self.client_secret_list:
if token.startswith(secret):
logging.debug("Found match for secret %s of length %d" % (secret, len(secret)))
# In this case, the token is the email, since we don't actually
# have the user email
return token
# If we get here, we have not returned, so the token did not start with
# a valid secret
raise ValueError("Invalid token %s, not found in list of length %d" %
(token, len(self.client_secret_list)))

0 comments on commit 460b9e4

Please sign in to comment.