24.2 Sample OAuth 2.0 Application Integrated with Advanced Authentication

To create a sample web application, you need Python v3 (the sample script prepared on v3.4.3).

The following web application describes the functionalities supported when Advanced Authentication is integrated with OAuth 2.0. OAuth 2.0 server is an authorization and resource server. As an Authorization Server, the OAuth server can prompt the users to go through authentication chains and as a resource server, the OAuth server can prompt the users to provide user details.

You must create the following five files:

  1. Sample script (oauth2_test.py)

    from bottle import Bottle, request, run, redirect, SimpleTemplate, template
    from urllib.parse import urlparse, urlunparse, urlencode,quote
    import urllib.request
    import base64
    import ssl
    import json
    
    app = Bottle()
    
    client_id = 'id-rSCzuBLQgXCATfkXZ4fsedAo8sPsWxSs'
    client_secret = 'secret-91DpzWFD26RriURR7KJ1pryFx7V9QeDm'
    redirect_uri = 'http://localhost:8088/'  # this app callback URI
    authorization_endpoint = 'https://192.168.0.151/osp/a/TOP/auth/oauth2/grant'
    attributes_endpoint = 'https://192.168.0.151/osp/a/TOP/auth/oauth2/getattributes'
    state = {}
    
    @app.get('/getattr')
    def get_attributes():
        params = urlencode({
            'attributes': 'client username userRepository user_dn user_cn mail sid upn netbiosName',
            'access_token': state['access_token']
        })
        url = attributes_endpoint + '?' + params
        print('getattr url: {}\n'.format(url))
        req = urllib.request.Request(url)
        gcontext = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)  # avoid cert checking
        with urllib.request.urlopen(req, context=gcontext) as response:  # perform GET request and read response
            rsp = response.read()
        attributes = json.loads(rsp.decode('utf-8'))
        return template('attributes.html', items=attributes.items(), refresh_token=urllib.parse.quote(state['refresh_token']))
    
    
    @app.get('/')
    def do_get():
        code = request.query.get('code')
        if code:
            # got code from OAuth 2 authentication server
            token = get_token_code(code)
            state.update(token)
            return template('token.html', items=token.items(), refresh_token=urllib.parse.quote(token['refresh_token']))
        else:
            return template('main.html')
    
    
    @app.get('/logon')
    def do_logon():
        pr=list(urlparse(authorization_endpoint))
        # set query
        pr[4]=urlencode({
            'response_type': 'code',
            'client_id': client_id,
            'redirect_uri': redirect_uri
        })
        # perform redirection to OAuth 2 authentication server
        redirect(urlunparse(pr))
    
    @app.get('/logon-implicit')
    def do_logon_implicit():
        # parse authorization_endpoint URL
        pr = list(urlparse(authorization_endpoint))
        # set query
        pr[4] = urlencode({
            'response_type': 'token',
            'client_id': client_id,
        })
        # perform redirection to OAuth 2 authentication server
        redirect(urlunparse(pr))
    
    @app.get('/logon-creds')
    def do_logon_creds():
        return template('logonform.html')
    
    @app.post('/logon-creds')
    def do_logon_creds_post():
        username = request.forms.get('username')
        password = request.forms.get('password')
        token = get_token_password(username, password)
        state.update(token)
        return template('token.html', items=token.items(), refresh_token=urllib.parse.quote(token['refresh_token']))
    
    def get_token_password(username, password):
        # prepare POST parameters - encode them to urlencoded
        data = urlencode({
            'grant_type': 'password',
            'username': username,
            'password': password
        })
        data = data.encode('ascii')  # data should be bytes
        resp_text = post_data(data, prepare_headers())
        print(resp_text)
        return json.loads(resp_text)
    
    @app.get('/refresh')
    def do_refresh():
        token = refresh_access_token(request.query.get('refresh_token'))
        state.update(token)
        return template('token.html', items=token.items(), refresh_token=state.get('refresh_token', ''))
    
    def get_token_code(code):
        # prepare POST parameters - encode them to urlencoded
        data = urlencode({
            'grant_type': 'authorization_code',
            'code': code,
            'redirect_uri': redirect_uri
        })
        data = data.encode('ascii')  # data should be bytes
        resp_text = post_data(data, prepare_headers())
        print(resp_text)
        return json.loads(resp_text)
    
    def refresh_access_token(refresh_token):
        print('refresh_token: {}'.format(refresh_token))
        # prepare POST parameters - encode them to urlencoded
        data = urlencode({
            'grant_type': 'refresh_token',
            'refresh_token': refresh_token,
        })
        data = data.encode('ascii')  # data should be bytes
        resp_text = post_data(data, prepare_headers())
        print(resp_text)
        return json.loads(resp_text)
    
    def prepare_headers(use_content_type_hdr = True):
        hdrs = {
            'Authorization': 'Basic {}'.format(base64.b64encode(
                '{}:{}'.format(quote(client_id, safe=''), quote(client_secret, safe='')).encode('ascii')).decode(
                'ascii')),
        }
        if use_content_type_hdr:
            hdrs.update({'Content-type': 'application/x-www-form-urlencoded'})
        return hdrs
    
    def post_data(data, headers):
        print('post_data\nheaders:\n{}\ndata:\n{}'.format(headers, data))
        req = urllib.request.Request(authorization_endpoint, data, headers)
        gcontext = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)  # avoid cert checking
        with urllib.request.urlopen(req, context=gcontext) as response:  # perform POST request and read response
            rsp = response.read()
        return rsp.decode('utf-8')
    
    
    run(app, host='0.0.0.0', port=8088)
     

    NOTE:In the script, you must change the values for client_id, client_secret, and Advanced Authentication server address in authorization_endpoint and attributes_endpoint (lines 10-14).

  2. Main menu (main.html)

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
        <script type="text/javascript">
             //<![CDATA[
                function getHashParam(name) {
                    var hash = window.location.hash;
                    if (hash) {
                        if (name = (new RegExp('[#&]' + encodeURIComponent(name) + '=([^&]*)')).exec(hash))
                        return decodeURIComponent(name[1]);
                    }
                }
                function showResult() {
                    if (window.location.hash) {
                        document.getElementById('result').innerHTML = '<table border="1">'+
                            '<tr><td>access_token</td><td>'+getHashParam('access_token')+'</td></tr>'+
                            '<tr><td>token_type</td><td>'+getHashParam('token_type')+'</td></tr>'+
                            '<tr><td>expires_in</td><td>'+getHashParam('expires_in')+'</td></tr>'+
                            '</table>';
                    } else {
                        document.getElementById('result').innerHTML = 'Implicit granted token is not found';
                    }
                }
              ]]//>
            </script>
    </head>
    <body onload="showResult();">
    <div id="result">result</div><br/>
    <br/>
    Click <a href="/logon">here</a> to obtain an authentication token through Authorization Code Grant<br/>
    Click <a href="/logon-implicit">here</a> to obtain an authentication token through Implicit Grant (the token will be received in hash part of THIS page)<br/>
    Click <a href="/logon-creds">here</a> to obtain an authentication token through Resource Owner Password Credentials Grant<br/>
    </body>
    </html>
  3. Token information (token.html)

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
    Token<br/>
    <table border="1">
        % for k, v in items:
        <tr>
            <td>{{k}}</td>
            <td>{{v}}</td>
        </tr>
        % end
    </table>
    <br/>
    <a href="/getattr">Get attributes</a><br/>
    <a href="/refresh?refresh_token={{refresh_token}}">Refresh token</a>
    </body>
    </html> 
  4. Attributes information (attributes.html)

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
    Attributes<br/>
    <table border="1">
        % for k, v in items:
        <tr>
            <td>{{k}}</td>
            <td>{{v}}</td>
        </tr>
        % end
    </table>
    <br/>
    <a href="/refresh?refresh_token={{refresh_token}}">Refresh token</a>
    </body>
    </html>
  5. Logon form for Resource Owner Password Credentials Grant mode (logonform.html)

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
    <form method="post" action="/logon-creds">
        User name: <input type="text" name="username"><br/>
        Password: <input type="password" name="password"><br/>
        <input type="submit">
    </form>
    </body>
    </html> 

24.2.1 Running the Sample Web Application

Perform the following steps to run the sample web application.

  1. Run the script python oauth2_test.py.

  2. Open the URL http://localhost:8088.

    A message is displayed with the following modes:

    Authorization Code Grant
    Implicit Grant (the token will be received in hash part of THIS page)
    Resource Owner Password Credentials Grant (is not supported by default but it can be activated in AAF)
  3. Select the grant based on your requirement.

    • Authorization Code Grant

      1. Ensure that Use for Owner Password Credentials is set to OFF in the Advanced settings section for the OAuth 2.0 event.

      2. Click the first link.

        The NetIQ Access page is displayed with the user name request.

      3. Specify the Username.

      4. Click Next.

      5. Authenticate using all required methods of the chain.

        The result page shows the access_token, token_type and expires_in.

        • Click Get attributes to look at the attributes.

        • Click Refresh token to refresh token. The access_token value is updated.

    • Implicit Grant

      1. Ensure that Use for Owner Password Credentials is set to OFF in the Advanced settings section for the OAUTH 2.0 event.

      2. Click the first link.

        The NetIQ Access page is displayed with the user name request.

      3. Specify the Username.

      4. Click Next.

      5. Authenticate using all the required methods of the chain.

        The result page shows the access_token, token_type and expires_in.

    • Resource Owner Password Credentials Grant

      1. Open Advanced settings for the OAUTH 2.0 event.

      2. Set Use for Owner Password Credentials to ON.

      3. Click the third link.

        A request for Username and Password is displayed.

      4. Specify the username and password, then click Submit.

        The result page displays the access_token, token_type, and expires_in.