Daniel Watrous on Software Engineering

A Collection of Software Problems and Solutions

Posts tagged authorization

Software Engineering

JWT based authentication in Python bottle

May applications require authentication to secure protected resources. While standards like oAuth accommodate sharing resources between applications, more variance exists in implementations of securing the app in the first place. A recent standard, JWT, provides a mechanism for creating tokens with embedded data, signing these tokens and even encrypting them when warranted.

This post explores how individual resource functions can be protected using JWT. The solution involves first creating a function decorator to perform the authentication step. Each protected resource call is then decorated with the authentication function and subsequent authorization can be performed against the data in the JWT. Let’s first look at the decorator.

jwtsecret = config.authentication.get('jwtsecret')
 
class AuthorizationError(Exception):
    """ A base class for exceptions used by bottle. """
    pass
 
def jwt_token_from_header():
    auth = bottle.request.headers.get('Authorization', None)
    if not auth:
        raise AuthorizationError({'code': 'authorization_header_missing', 'description': 'Authorization header is expected'})
 
    parts = auth.split()
 
    if parts[0].lower() != 'bearer':
        raise AuthorizationError({'code': 'invalid_header', 'description': 'Authorization header must start with Bearer'})
    elif len(parts) == 1:
        raise AuthorizationError({'code': 'invalid_header', 'description': 'Token not found'})
    elif len(parts) > 2:
        raise AuthorizationError({'code': 'invalid_header', 'description': 'Authorization header must be Bearer + \s + token'})
 
    return parts[1]
 
def requires_auth(f):
    """Provides JWT based authentication for any decorated function assuming credentials available in an "Authorization" header"""
    def decorated(*args, **kwargs):
        try:
            token = jwt_token_from_header()
        except AuthorizationError, reason:
            bottle.abort(400, reason.message)
 
        try:
            token_decoded = jwt.decode(token, jwtsecret)    # throw away value
        except jwt.ExpiredSignature:
            bottle.abort(401, {'code': 'token_expired', 'description': 'token is expired'})
        except jwt.DecodeError, message:
            bottle.abort(401, {'code': 'token_invalid', 'description': message.message})
 
        return f(*args, **kwargs)
 
    return decorated

In the above code the requires_auth(f) function makes use of a helper function to verify that there is an Authorization header and that it appears to contain the expected token. A custom exception is used to indicate a failure to identify a token in the header.

The requires_auth function then uses the python JWT library to decode the key based on a secret value jwtsecret. The secret is obtained from a config object. Assuming the JWT decodes and is not expired, the decorated function will then be called.

Authenticate

The following function can be use to generate a new JWT.

jwtexpireoffset = config.authentication.get('jwtexpireoffset')
jwtalgorithm = config.authentication.get('jwtalgorithm')
 
def build_profile(credentials):
    return {'user': credentials['user'],
            'role1': credentials['role1'],
            'role2': credentials['role2'],
            'exp': time.time()+jwtexpireoffset}
 
bottle.post('/authenticate')
def authenticate():
    # extract credentials from the request
    credentials = bottle.request.json
    if not credentials or 'user' not in credentials or 'password' not in credentials:
        bottle.abort(400, 'Missing or bad credentials')
 
    # authenticate against some identity source, such as LDAP or a database
    try:
        # query database for username and confirm password
        # or send a query to LDAP or oAuth
    except Exception, error_message:
        logging.exception("Authentication failure")
        bottle.abort(403, 'Authentication failed for %s: %s' % (credentials['user'], error_message))
 
    credentials['role1'] = is_authorized_role1(credentials['user'])
    credentials['role2'] = is_authorized_role2(credentials['user'])
    token = jwt.encode(build_profile(credentials), jwtsecret, algorithm=jwtalgorithm)
 
    logging.info('Authentication successful for %s' % (credentials['user']))
    return {'token': token}

Notice that two additional values are stored in the global configuration, jwtalgorithm and jwtexpireoffset. These are used along with jwtsecret to encode the JWT token. The actual verification of user credentials can happen in many ways, including direct access to a datastore, LDAP, oAuth, etc. After authenticating credentials, it’s easy to authorize a user based on roles. These could be implemented as separate functions and could confirm role based access based on LDAP group membership, database records, oAuth scopes, etc. While the role level access shown above looks binary, it could easily be more granular. Since a JWT is based on JSON, the JWT payload is represented as a JSON serializable python dictionary. Finally the token is returned.

Protected resources

At this point, any protected resource can be decorated, as shown below.

def get_jwt_credentials():
    # get and decode the current token
    token = jwt_token_from_header()
    credentials = jwt.decode(token, jwtsecret)
    return credentials
 
@appv1.get('/protected/resource')
@requires_auth
def get_protected_resource():
    # get user details from JWT
    authenticated_user = get_jwt_credentials()
 
    # get protected resource
    try:
        return {'resource': somedao.find_protected_resource_by_username(authenticated_user['username'])}
    except Exception, e:
        logging.exception("Resource not found")
        bottle.abort(404, 'No resource for username %s was found.' % authenticated_user['username'])

The function get_protected_resource will only be executed if requires_auth successfully validates a JWT in the header of the request. The function get_jwt_credentials will actually retrieve the JWT payload to be used in the function. While I don’t show an implementation of somedao, it is simply an encapsulation point to facilitate access to resources.

Since the JWT expires (optionally, but a good idea), it’s necessary to build in some way to extend the ‘session’. For this a refresh endpoint can be provided as follows.

bottle.post('/authenticate/refresh')
@requires_auth
def refresh_token():
    """refresh the current JWT"""
    # get and decode the current token
    token = jwt_token_from_header()
    payload = jwt.decode(token, jwtsecret)
    # create a new token with a new exp time
    token = jwt.encode(build_profile(payload), jwtsecret, algorithm=jwtalgorithm)
 
    return {'token': token}

This simply repackages the same payload with a new expiration time.

Improvements

The need to explicitly refresh the JWT increases (possibly double) the number of requests made to an API only for the purpose of extending session life. This is inefficient and can lead to awkward UI design. If possible, it would be convenient to refactor requires_auth to perform the JWT refresh and add the new JWT to the header of the request that is about to be processed. The UI could then grab the updated JWT that is produced with each request to use for the subsequent request.

The design above will actually decode the JWT twice for any resource function that requires access to the JWT payload. If possible, it would be better to find some way to inject the JWT payload into the decorated function. Ideally this would be done in a way that functions which don’t need the JWT payload aren’t required to add it to their contract.

The authenticate function could be modified to return the JWT as a header, rather than in the body of the request. This may decrease the chances of a JWT being cached or logged. It would also simplify the UI if the authenticate and refresh functions both return the JWT in the same manner.

Extending

This same implementation could be reproduced in any language or framework. The basic steps are

  1. Perform (role based) authentication and authorization against some identity resource
  2. Generate a token (like JWT) indicating success and optionally containing information about the authenticated user
  3. Transmit the token and refreshed tokens in HTTP Authorization headers, both for authenticate and resource requests

Security and Risks

At the heart of JWT security is the secret used to sign the JWT. If this secret is too simple, or if it is leaked, it would be possible for a third party to craft a JWT with any desired payload, and trick an application into delivering protected resources to an attacker. It is important to choose strong secrets and to rotate them frequently. It would also be wise to perform additional validity steps. These might include tracking how many sessions a user has, where those session have originated and the nature and frequency/speed of requests. These additional measures could prevent attacks in the event that a JWT secret was discovered and may indicate a need to rotate a secret.

Microservices

In microservice environments, it is appealing to authenticate once and access multiple microservice endpoints using the same JWT. Since a JWT is stateless, each microservice only needs the JWT secret in order to validate the signature. This potentially increases the attach surface for a hacker who wants the JWT secret.

Software Engineering

Use oAuth to Register Users on My Site using Social Media Credentials

I’m interested in allowing a user to register on my site/app using their social account credentials (e.g. Google, Facebook, LinkedIn, etc.). It should also be possible to register using an email address. Since the site/app will be composed of a handful of microservices, I would want to provide my own identity service, which might includes profile information and roles. This should be possible with oAuth.

I found plenty of examples of how to use oAuth against someone’s social accounts. What I didn’t find were any examples of how to manage user registration and possibly ongoing authentication against a social account. I also didn’t see an examples of how to mix a social oAuth server with an internal oAuth server. The internal oAuth server would provide authentication for each microservice consumed by the site/app. It seemed awkward to keep validating access tokens against the social account oAuth server for each request to local microservices, so this design uses the social access token to get an access token against the internal oAuth server. Here’s how that looks:

social-oauth-register-third-party (You can play with this diagram here)

As you can see, the access token is used to get the initial data to create a user (register) in the internal oAuth server. After registration, the user can still authenticate using their social account, but the account wouldn’t be created a second time. Also notice that the social access token is used to generate the authorization code and eventually the access token for the internal oAuth server, instead of going back to the user for confirmation. In other words, a valid access token from the social oAuth server presumes the user has logged in to authorize access already. The oAuth access token from the internal oAuth server is used to authentication all calls to internal microservices.

References:

http://nordicapis.com/how-to-control-user-identity-within-microservices/
http://www.bubblecode.net/en/2016/01/22/understanding-oauth2/
http://stackoverflow.com/questions/29644916/microservice-authentication-strategy

Software Engineering

Software licensing: Authentication and authorization for admin pages

For simplicity and security I’ve decided to integrate with the Google Account authentication mechanism that’s built into Google App Engine. This allows anyone with a Google account to login to my application without the need to setup another account. This also gives me access to the user’s valid email in order to send messages and other communication related to the service I provide.

So far I have three separate ‘areas’ for interfacing with my service. The first area is comprised of public pages, such as the home page or privacy policy. The next area is the API where RESTful access will take place. That leaves the administration area where an account administrator will be able to view statistics, adjust licenses, etc. These are mapped as follows

http://domain/
http://domain/api/
http://domain/admin/

The API will require authentication with each call in the form of an apikey (may change to oAuth in the future). I was able to secure the admin area of the site by adding a security-constraint to the web.xml file. Here’s what that looks like.

1
2
3
4
5
6
7
8
9
10
11
<web-app ...>
	...
	<security-constraint>
	   <web-resource-collection>
	       <url-pattern>/admin/*</url-pattern>
	   </web-resource-collection>
	   <auth-constraint>
	       <role-name>*</role-name>
	   </auth-constraint>
	</security-constraint>
</web-app>

You might have noticed this mechanism is not limited to authentication only. It’s also possible to include authorization preferences by role using role-name.