{"id":16901968,"url":"https://github.com/jpf/okta-oidc-beta","last_synced_at":"2025-08-20T12:42:46.068Z","repository":{"id":66595802,"uuid":"48073367","full_name":"jpf/okta-oidc-beta","owner":"jpf","description":"Instructions and sample code for Okta's OpenID Connect Beta","archived":false,"fork":false,"pushed_at":"2016-09-29T21:24:11.000Z","size":573,"stargazers_count":23,"open_issues_count":5,"forks_count":11,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-08-15T05:48:05.553Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jpf.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-12-15T22:45:33.000Z","updated_at":"2024-11-29T08:49:24.000Z","dependencies_parsed_at":null,"dependency_job_id":"5e9e51ba-92a4-498c-b1c7-ee58065e61b0","html_url":"https://github.com/jpf/okta-oidc-beta","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jpf/okta-oidc-beta","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jpf%2Fokta-oidc-beta","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jpf%2Fokta-oidc-beta/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jpf%2Fokta-oidc-beta/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jpf%2Fokta-oidc-beta/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jpf","download_url":"https://codeload.github.com/jpf/okta-oidc-beta/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jpf%2Fokta-oidc-beta/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271321165,"owners_count":24739472,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-20T02:00:09.606Z","response_time":69,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-13T18:03:09.957Z","updated_at":"2025-08-20T12:42:46.042Z","avatar_url":"https://github.com/jpf.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Introduction\n\nThank you for participating in Okta's OpenID Connect (OIDC) Beta.\n\nIn this document, we will show you how to get started using OpenID\nConnect with Okta. \n\nSpecifically demonstrated are the following two use-cases:\n\n1.  Using OpenID Connect to authenticate Okta users to a backend\n    web server (If your familiar with SAML, this is roughly\n    equivalent to \"SP-initiated SAML\").\n2.  Using OpenID Connect with a JavaScript Single Page Application.\n\nIf you have any questions, comments, or suggestions for this\ndocument please contact Joël Franusic \u003cjoel.franusic@okta.com\u003e\n\n# How to run this demo\n\nThis repository comes with code that you can run yourself to see how\nOIDC works. You can run this code locally on your machine, or you\ncan deploy the code to Heroku.\n\n## Prerequisites\n\nAll examples in this guide assume that you have an Okta org, API\ntoken, and Okta OAuth Client ID. \n\nRunning the code samples locally will require the use of Python and\nthe [pip](https://en.wikipedia.org/wiki/Pip_%28package_manager%29) package manager.\n\nHere is how to get those things if you do not have them already:\n\n### Okta org\n\nIf you do not have an Okta org, you can [sign up for a free Developer\nEdition Okta org](https://www.okta.com/developer/signup/).\n\n### Okta API token\n\nIf you do not have an API token, follow our guide for\n[getting a token](http://developer.okta.com/docs/api/getting_started/getting_a_token.html).\n\n### Okta OAuth Client ID\n\n\nThe easiest way to register an OAuth client is by creating a new\napplication integration from the Okta Admin interface:\n\n1.  Log in to your Okta org as an administrator.\n2.  After logging in, click on the \"Admin\" button.\n3.  From the \"Shortcuts\" on the right hand side of the screen,\n    click \"Add Applications\"\n4.  Click the \"Create New App\" button, which is on the left hand\n    side of the screen.\n5.  **Important:** In the \"Platform\" section, select \"Single Page App (SPA)\"\n6.  Select \"OpenID Connect\" as the \"Sign on method\"\n7.  Configure one or more \"Redirect URIs\" for your application.\n8.  Click \"Finish\"\n9.  Using the \"People\" or \"Groups\" tabs, assign people to your\n    newly created application. \n    **Note:** Users will not be able to authenticate to your\n    application if they are not assigned!\n10. Find your \"Client ID\" in the \"Client Credentials\" section of\n    the \"Groups\" tab.\n\nThe Client ID that you just obtained is what you will use to\nconfigure the demo application in this guide, or to integrate your\napplication with Okta.\n\n### Python\n\nWhile the code samples in this guide are written in Python, you do\nnot need Python to use OpenID Connect with Okta.\n\nTo run the code samples in this project you will need a working\ncopy of Python 2.7+ and the pip package manager. See this guide on\n\"[Properly Installing Python](http://docs.python-guide.org/en/latest/starting/installation/)\"  for instructions for setting up\nPython on your system.\n\n## Setup\n\nOnce you have all the prerequisites, the next step will be do get\nthis example code running. You can either run this code from your\nlocal machine, or by running the code from Heroku.\n\n## Make a local copy of this repository\n\nBefore you can make use of the code in this guide, you will need a\nlocal copy of the code to work from. You can either download a copy\nof this repository using the \"Download ZIP\" button or using the `git\n   clone` command.\n\nUsing `git clone` is the suggested method for making a local copy of\nthis repository, because getting updates to this repository will be\nas easy as running `git pull origin master`.\n\n## Running on your local machine\n\nWith a local copy of this repository on your machine, the next step\nwill be to set up the project.\n\nYou can do this on Mac OS X and Linux by running these commands from the shell:\n\n    $ virtualenv venv\n    $ source venv/bin/activate\n    $ pip install -r requirements.txt\n\nIf you are using Homebrew on OS X, you *might* need to follow the\n[Homebrew specific installation instructions](http://cryptography.readthedocs.org/en/latest/installation/#building-cryptography-on-os-x) to install the Python `cryptography` library:\n\n    $ env CRYPTOGRAPHY_OSX_NO_LINK_FLAGS=1 LDFLAGS=\"$(brew --prefix openssl)/lib/libssl.a $(brew --prefix openssl)/lib/libcrypto.a\" CFLAGS=\"-I$(brew --prefix openssl)/include\" pip install cryptography\n\nOn OS X or Linux, **replace the example values in the commands below\nwith your data** and then run the modified commands in your shell to\nconfigure the application:\n\n    $ export OKTA_API_TOKEN=00A0B12CDefGHijkLmN3OPQRsTu4VWxyzABCdEf56G\n    $ export OKTA_BASE_URL=https://example.okta.com\n    $ export OKTA_CLIENT_ID=aBcDEfG0HiJkL1mn2oP3\n\nUse this command to run the application locally on your system:\n\n    $ python app.py\n\n### Make the example available via HTTPS using ngrok\n\nAs a last step, you will need to make your local copy of the\nexample code available via HTTPS. You need to do this because the\n[OpenID Connect specification requires that you do so](http://openid.net/specs/openid-connect-core-1_0.html#ImplicitAuthRequest). \n\nThe easiest way to do this is using the excellent tool \"[ngrok](https://ngrok.com/)\".\n\nTo get started with ngrok, visit the\n[\"Download\" page for ngrok](https://ngrok.com/download), download ngrok, then start it on your\nsystem.\n\nAssuming that your example code is listening on\n`http://localhost:5000`, start ngrok with the following command:\n\n    $ ngrok http 5000\n\nWhen ngrok starts, you will see a page that give you information\non the ngrok. Look for the line that starts with **Forwarding** and\nthen copy the URL that starts with \"https\", it will look something\nlike this: `https://ab123cd4.ngrok.io` - this is the URL that you\nwill use in the following steps.\n\n## Running on Heroku\n\nAssuming that you've already installed the\n[Heroku Toolbelt](https://toolbelt.heroku.com/), here are the commands you'd use to deploy this\napplication to Heroku:\n\n    $ heroku create\n    $ git push heroku master\n\nThen, configure the application using these commands below. \n**Make sure to replace the values below with your data!**\n\n    $ heroku config:set OKTA_API_TOKEN=00A0B12CDefGHijkLmN3OPQRsTu4VWxyzABCdEf56G\n    $ heroku config:set OKTA_BASE_URL=https://example.okta.com\n    $ heroku config:set OKTA_CLIENT_ID=aBcDEfG0HiJkL1mn2oP3\n\nFinally:\n\n    $ heroku open\n\n## Whitelist URL in Okta\n\nThe last thing that you will need to do is add the URL for your\nexample application to the appropriate Okta whitelists. This is\ndone in two places: \n\n1.  The OAuth client configuration in your Okta org\n2.  The CORS settings in your Okta org\n\nIf you're using ngrok or Heroku to host your example application,\nthen your URL will look like this \"`https://abc123de4.ngrok.io`\" or\n \"`https://example.herokuapp.com`\".\n\n### Update the OAuth Client `redirect_uris` array\n\nIf you didn't do it when you created your OAuth Client ID (See section 2.1.3), you\nwill need to go back to that section and follow the instructions\nto add your URL to the `redirect_uris` whitelist.\n\n### Update CORS configuration on the Okta web page\n\nYou will also need to enable the URL for CORS. See \n[Okta's guide to Enabling CORS](http://developer.okta.com/docs/api/getting_started/enabling_cors.html) for details on how to do this.\n\n## Open the URL for the example application in your browser\n\nIf you're using ngrok or Heroku to host your example application,\nthen your URL will look like this \"`https://abc123de4.ngrok.io`\" or\n \"`https://example.herokuapp.com`\".\n\n# How it works\n\nThe core of using Open ID Connect with your application is the\n`id_token`, which is a JSON Web Token (JWT).\n\nBelow is an example of what a JWT looks like:\n\n    eyJhbGciOiJSUzI1NiJ9.eyJ2ZXIiOjEsImlzcyI6Imh0dHBzOi8vZXhhbXBsZS5va3RhLmNvbSIsIn\n    N1YiI6IjAwdTBhYmNkZWZHSElKS0xNTk9QIiwibG9naW4iOiJ1c2VybmFtZUBleGFtcGxlLmNvbSIsI\n    mF1ZCI6IkFiY0RFMGZHSEkxamsyTE0zNG5vIiwiaWF0IjoxNDQ5Njk1NjAwLCJleHAiOjE0NDk2OTky\n    MDAsImFtciI6WyJwd2QiXSwiYXV0aF90aW1lIjoxNDQ5Njk1NjAwfQ.btq43W2-SOsc7BA_SyMPEKcu\n    2xUYoyLuY948k6tWzZAsy__MndK9pX3WjYYMwkGqfthLjMWXMuYem2-uWcdwfDCDpWoxK4Es3N8dnsQ\n    NeS_U0_FfVZfkj_OMGw28RPDLRErNAuyXFj2DegXUh74PEZcDaKSz5-17znEpXgzbT14\n\n**Note:** The line breaks have been added for readability.\n\nA JWT is, essentially, a base64 encoded JSON object. Here is what\nthe JWT above looks like after it has been decoded and validated:\n\n    {\n      \"ver\": 1,\n      \"iss\": \"https://example.okta.com\",\n      \"sub\": \"00u0abcdefGHIJKLMNOP\",\n      \"login\": \"username@example.com\",\n      \"aud\": \"AbcDE0fGHI1jk2LM34no\",\n      \"iat\": 1449695600,\n      \"exp\": 1449699200,\n      \"amr\": [\n        \"pwd\"\n      ],\n      \"auth_time\": 1449695600\n    }\n\n# Getting an id\\_token from Okta\n\nThe easiest way to get an `id_token` from Okta is to use the Okta\nSign-In Widget. Here is how to configure the Okta Sign-In Widget\nto give you an `id_token`:\n\n    function setupOktaSignIn(baseUrl, clientId) {\n        var oktaSignIn = new OktaSignIn({\n            baseUrl: baseUrl,\n            clientId: clientId,\n            authParams: {\n                responseType: 'id_token',\n                responseMode: 'okta_post_message',\n                scope: ['openid', 'groups']\n            },\n            idps: [\n                {\n                    'type': 'FACEBOOK',\n                    'id': '0oa5c17af3cHZliYY0h7'\n                },\n                {\n                    'type': 'GOOGLE',\n                    'id': '0oa5c17af3cHZliYY0h8'\n                },\n                {\n                    'type': 'LINKEDIN',\n                    'id': '0oa5c17af3cHZliYY0h8'\n                }\n            ]\n        });\n        return oktaSignIn;\n    };\n\nNote: Other valid types for `authParams.scope` are: `openid`,\n`email`, `profile`, `address`, `phone`, and `groups`.\n\n# Use cases\n\nThe OpenID Connect specification makes provisions for many different\nuse cases. For this beta, we are support two use cases:\n\n1.  Server-side web application\n    Authenticating against a web application that runs on a server.\n2.  Single Page Application\n    Authenticating a client-side JavaScript application that runs in\n    a web browser.\n\n## Server-side web application\n\nThis use case demonstrates how to have a server-side web\napplication authenticate users via OpenID Connect. If you are\nfamiliar with SAML, this is the same use case as \"SP initiated\nSAML\".\n\nAuthenticating Okta users against your server-side web application\nconsists of these core steps:\n\n1.  Okta authenticates a user.\n2.  Upon a successful authentication, Okta issues the user an OIDC\n    `id_token` and direct the users browser to deliver the\n    `id_token` to your web application.\n3.  Your server-side web application will validate the `id_token`\n    and, if the token is valid, will create a session for the user\n    so that the user is \"logged in\" to your web application.\n\nStep 2 is covered below in the \"Getting an OIDC `id_token` from\nOkta\" section and Step 3 is covered in the \"Validating an OIDC\n`id_token` from Okta\" section.\n\n### Getting an OIDC `id_token` from Okta\n\nCurrently, there are three ways to get an `id_token` from Okta,\nsorted in order if \"ease of implementation\":\n\n1.  Having users click on a special link that will redirect them\n    through Okta.\n2.  Authenticating users via the Okta Sign-In Widget.\n3.  Authenticating users via [/authn](http://developer.okta.com/docs/api/resources/authn.html) and [/oauth2](http://developer.okta.com/docs/api/resources/oidc.html) Okta API endpoints.\n\nWhich method you select depends on how customized you want the\nuser's login experience to be. \n\nIf you don't care about a customized login experience, the easiest\nway to get an `id_token` from Okta is to have users click on a\nspecial link that will redirect them through Okta to your\napplication.\n\nThe Okta Sign-In Widget handles all possible user states and is\nmoderately customizable. It is a good choice if you don't have\nextremely detailed design requirements.\n\nUsing the Okta API endpoints directly gives you the most\nflexibility in terms of customization at the expense of requiring\nyou to support all of the possible flows that your users will go\nthrough.\n\nDetails on each of these methods are below:\n\n### Getting an `id_token` via a special Okta link\n\nIf you don't mind your users seeing an Okta branded login page,\nhaving your users login to your application using the OpenID\nConnect \"Code Flow\".\n\nThe basics of implementing the Code Flow are below. For more\ninformation in the Code Flow, we suggest reading the \"OpenID Connect Basic Client\nImplementer's Guide\", which contains a good [guide to implementing the\nOIDC Code Flow](https://openid.net/specs/openid-connect-basic-1_0.html#CodeFlow).\n\nSee our [OIDC documentation for details on the request parameters](http://developer.okta.com/docs/api/resources/oidc.html#request-parameters)\nfor more details on how Okta uses the OIDC request parameters.\n\nBelow is an example of what this link might look like:\n\n    https://example.okta.com/oauth2/v1/authorize?nonce=FakeNonce\u0026state=FakeState\u0026redirect_uri=https%3A%2F%2Fexample.com%2Fsse%2Foidc\u0026response_type=id_token\u0026client_id=a0bcdEfGhIJkLmNOPQr1\u0026scope=openid\u0026response_mode=form_post\n\n### Getting an `id_token` via the Okta Sign-In Widget\n\nThe easiest way customize the login experience that your users\nsee is to use the [Okta Sign-In Widget](http://developer.okta.com/docs/guides/okta_sign-in_widget.html).\n\nTo use the Okta Sign-In Widget with your application, follow the\n[guide for setting up the Okta Sign-In Widget](http://developer.okta.com/docs/guides/okta_sign-in_widget.html) but make the\nfollowing two changes to your configuration of the Okta Sign-In Widget:\n\n1.  Configure the Sign-In Widget to request an OIDC `id_token`:\n    \n        var oktaSignIn = new OktaSignIn({\n            baseUrl: baseUrl,\n            clientId: clientId,\n            authParams: {\n                responseType: 'id_token',\n                responseMode: 'okta_post_message',\n                scope: ['openid', 'groups']\n            },\n            idps: [\n                {\n                    'type': 'FACEBOOK',\n                    'id': '0oa5c17af3cHZliYY0h7'\n                },\n                {\n                    'type': 'GOOGLE',\n                    'id': '0oa5c17af3cHZliYY0h8'\n                },\n                {\n                    'type': 'LINKEDIN',\n                    'id': '0oa5c17af3cHZliYY0h8'\n                }\n            ]\n        });\n2.  Add a \"SUCCESS\" handler to the widget which will extract the\n    `id_token` and pass it on to your application backend service.\n    \n    Here is how this is done in the example application in this project:\n    \n        oktaSignIn.renderEl(\n          { el: '#okta-sign-in-widget' },\n         function (res) {\n            console.log(res);\n            var id_token = res.id_token || res.idToken;\n            if (res.status === 'SUCCESS') {\n              $.post(\"/sso/oidc\", {\"id_token\": id_token}, function(data) {\n                window.location.href=\"/secret\";\n              });\n            }\n          },\n         function (err) { console.log('Unexpected error authenticating user: %o', err); }\n        );\n\n### Getting an `id_token` via Okta API endpoints\n\nLastly, if you need to make customizations to the login\nexperience beyond what the Sign-In Widget allows, you can do that\nby making API requests directly to the Okta API.\n\nAt a high level, what you will need to do is write some code on\nyour application backend that will do the following:\n\n-   Accepts a **username** and **password**\n-   Uses the **username** and **password** to make a request to Okta's\n    `/authn` API endpoint and extracts the `sessionToken` from the\n    results of a successful request.\n-   Redirects the user to an Okta's `/oauth2/v1/authorize` API\n    endpoint using the `sessionToken` in the request parameters.\n\nHere is how this is done in the example application:\n\n    @app.route(\"/login\", methods=['POST'])\n    def login_with_password():\n        payload = {\n            'username': request.form['username'],\n            'password': request.form['password'],\n            }\n    \n        authn_url = \"{}/api/v1/authn\".format(okta['base_url'])\n        r = requests.post(authn_url, headers=headers, data=json.dumps(payload))\n        result = r.json()\n    \n        if 'errorCode' in result:\n            flash(result['errorSummary'])\n            return redirect(url_for('main_page', _external=True, _scheme='https'))\n    \n        redirect_uri = url_for(\n            'sso_oidc',\n            _external=True,\n            _scheme='https')\n        redirect_url = create_authorize_url(\n            base_url=okta['base_url'],\n            sessionToken=result['sessionToken'],\n            client_id=okta['client_id'],\n            scope='openid',\n            response_type='id_token',\n            response_mode='form_post',\n            nonce='FakeNonce',\n            state='FakeState',\n            redirect_uri=redirect_uri,\n            )\n        return redirect(redirect_url)\n\nAnd here is the `create_authorize_url` function that is used to\nconstruct the request to `/oauth2/v1/authorize` with the proper\nrequest parameters:\n\n    def create_authorize_url(**kwargs):\n        base_url = kwargs['base_url']\n        del(kwargs['base_url'])\n        redirect_url = \"{}/oauth2/v1/authorize?{}\".format(\n            base_url,\n            urllib.urlencode(kwargs),\n        )\n        return redirect_url\n\n### Validating an OIDC `id_token` from Okta\n\nAn OIDC `id_token` is a JWT and validating a JWT is easy. Below is a\ndemonstration of how to validate a JWT in Python using the [pyjwt](https://github.com/jpadilla/pyjwt#pyjwt)\nPython library.\n\n(See [JWT.io](http://jwt.io/#libraries-io) for a list of JWT libraries in your favorite language.)\n\nThe [pyjwt](https://github.com/jpadilla/pyjwt#pyjwt) library handles a lot of ancillary JWT validation by\ndefault. In particular, it validates the `audience` attribute,\nwhich means that it will return an error unless the value\n`audience` attribute matches what we pass into this method.\n\nHere is how we parse a JWT in this sample application:\n\n    def parse_jwt(id_token):\n        public_key = fetch_jwt_public_key_for(id_token)\n        rv = jwt.decode(\n            id_token,\n            public_key,\n            algorithms='RS256',\n            issuer=okta['base_url'],\n            audience=okta['client_id'])\n        return rv\n\nHere is base test that we use for the `parse_jwt` function:\n\n    @responses.activate\n    def test_parse_jwt_valid(self):\n        id_token = self.create_jwt(claims={})\n        rv = flask_app.parse_jwt(id_token)\n        self.assertEquals('00u0abcdefGHIJKLMNOP', rv['sub'])\n\nHere are some details on the parameters that we are explicitly\nsetting in `parse_jwt`:\n\n1.  Force the JWT signing algorithm to `RS256`\n    \n    This line forces the JWT signing algorithm to `RS256`:\n    \n        algorithms='RS256',\n    \n    We do this because it is a best practice for handling JWTs and\n    is done to avoid [critical vulnerabilities in JSON Web Token libraries](https://www.chosenplaintext.ca/2015/03/31/jwt-algorithm-confusion.html).\n\n2.  The OIDC Issuer\n    \n    This line sets the `issuer` to the value of the Okta Base URL,\n    which is what Okta uses as the `issuer`:\n    \n        issuer=okta['base_url'],\n    \n    And this is how we test that the JWT decoder is properly\n    validating the `issuer`:\n    \n        @responses.activate\n        @raises(jwt.JWTClaimsError)\n        def test_parse_jwt_invalid_issuer(self):\n            id_token = self.create_jwt(claims={'iss': 'https://invalid.okta.com'})\n            flask_app.parse_jwt(id_token)\n        \n        @responses.activate\n        @raises(ValueError)\n        def test_parse_jwt_invalid_issuer_domain(self):\n            id_token = self.create_jwt(\n                claims={'iss': 'https://invalid.example.com'},\n                kid='EXAMPLEKID')\n            flask_app.parse_jwt(id_token)\n\n3.  The OIDC Audience\n    \n    This line sets the `audience` to the value of the Okta OAuth\n    Client ID, which is what Okta uses as the `audience`:\n    \n        audience=okta['client_id'])\n    \n    And this is how we test that the JWT decoder is properly\n    validating the `audience`:\n    \n        @responses.activate\n        @raises(jwt.JWTClaimsError)\n        def test_parse_jwt_invalid_audience(self):\n            id_token = self.create_jwt(claims={'aud': 'INVALID'})\n            flask_app.parse_jwt(id_token)\n    \n    Okta uses the OAuth Client ID as the audience in the\n    `id_token` JWTs that it issues. We pass this value to `pyjwt` so\n    that our JWTs are properly validated.\n\nWhere does the `public_key` come from? It is fetched from the\n[Okta JSON Web Key endpoint](https://example.okta.com/oauth2/v1/keys) - which can be discovered via the\n[.well-known/openid-configuration](https://example.okta.com/.well-known/openid-configuration) URL.\n\nBelow is a demonstration of how to fetch the public key for\n`example.okta.com` using the command line (on OS X).\n\nOn the first line, we pull down the JSON from\n`.well-known/openid-configuration` and pull out the `jwks_uri`\nelement using `grep` and a regular expression (the \"[jq](https://github.com/stedolan/jq)\" command line\ntool is better suited for this, but not installed by\ndefault). Once we have the `jwks_uri`, we use that to fetch the\nkey from Okta, pull out the `x5c` key using grep, base64 decode\nthe `x5c` key, then pipe that to `openssl` to extract the public key.\n\n    JWKS_URI=`curl -s https://example.okta.com/.well-known/openid-configuration | egrep -o 'jwks_uri\":\"[^\"]*' | cut -d '\"' -f 3`;\n    curl -s $JWKS_URI | egrep -o '\"x5c\":\\[\"[^]]*' | cut -d '\"' -f 4 | tr -d '\\' | base64 -D | openssl x509 -inform DER -pubkey -noout\n\n### Fetching public keys for OIDC in Python\n\nSince this example uses Python, below is an example of how to\nautodiscover the JWKS URL for an Okta OIDC endpoint by appending\nthe `/.well-known/openid-configuration`  string to the end of the\nURL that is in the `iss` OIDC claim.\n\nBelow is some Python code that demonstrates how to automatically\ndiscover the JWKS URL and parse the public keys from that URL in.\n\nThis is what it does:\n\n1.  Checks the `id_token` header for a `kid` (\"Key ID\"). Fail validation\n    if the `id_token` doesn't have a `kid`.\n2.  Use the `kid` to see if we have previously cached the public key for that `kid`.\n    If we already have a public key, then use that to validate the\n    `id_token`.\n\nHere an example in Python that checks to see if the `id_token` has\na `kid`, and if so, checks if we've seen the public key for that\n`kid` before:\n\n    dirty_header = jws.get_unverified_header(id_token)\n    cleaned_key_id = None\n    if 'kid' in dirty_header:\n        dirty_key_id = dirty_header['kid']\n        cleaned_key_id = re.sub(not_alpha_numeric, '', dirty_key_id)\n    else:\n        raise ValueError('The id_token header must contain a \"kid\"')\n    if cleaned_key_id in public_keys:\n        return public_keys[cleaned_key_id]\n\n\u003e **Note**: In this example, we are using a Python Dictionary called\n\u003e `public_keys` as our hash. In production you should use whatever\n\u003e existing caching infrastructure you have in place.\n\nIf we haven't yet seen a public key matching the `kid`, then we\nfetch the public key as follows:\n\n1.  Take the URL from the `iss` claim in the `id_token`.\n2.  Validate that the domain name in the `iss` claim is a valid\n    Okta domain name.\n3.  If the domain name is valid, append\n    `/.well-known/openid-configuration` to the end of the URL in\n    the `iss` claim.\n4.  Fetch the URL above and take the `jwks_uri` key from the results.\n5.  Fetch the `jwks_uri` and, for each key in the result, it do\n    the following:\n    -   Take the first element in the `x5c` value.\n    -   Base64 decode the DER encoded x509 certificate.\n    -   Parse the DER encoded x509 certificate using the Python\n        `cryptography` library.\n    -   Store the public key in a hash, using the `kid` as the\n        value.\n\nHere an example in Python that does what is described above:\n\n    unverified_claims = jwt.get_unverified_claims(id_token)\n    dirty_url = urlparse.urlparse(unverified_claims['iss'])\n    if domain_name_for(dirty_url) not in allowed_domains:\n        raise ValueError('The domain in the issuer claim is not allowed')\n    cleaned_issuer = dirty_url.geturl()\n    oidc_discovery_url = \"{}/.well-known/openid-configuration\".format(\n        cleaned_issuer)\n    r = requests.get(oidc_discovery_url)\n    openid_configuration = r.json()\n    jwks_uri = openid_configuration['jwks_uri']\n    r = requests.get(jwks_uri)\n    jwks = r.json()\n    for key in jwks['keys']:\n        jwk_id = key['kid']\n        public_keys[jwk_id] = key\n\nWhen extracting the `iss` claim from the `id_token` we strongly\nurge you to treat that value as untrusted and validate the\ncontents before using it. \n\nIn the example above, we parse the URL in the `iss` claim and\ncheck that the domain matches \"okta.com\" or\n\"oktapreview.com\". This is done using the `domain_name_for`\nfunction below:\n\n    def domain_name_for(url):\n        second_to_last_element = -2\n        domain_parts = url.netloc.split('.')\n        (sld, tld) = domain_parts[second_to_last_element:]\n        return sld + '.' + tld\n\nFor more details on the `x5c` format, see the [\"x5c\" section in the\nJSON Web Key specification](https://tools.ietf.org/html/draft-ietf-jose-json-web-key-31#section-4.7), which is quoted below:\n\n\u003e The \"x5c\" (X.509 Certificate Chain) member contains a chain of one\n\u003e or more PKIX certificates [RFC5280].  The certificate chain is\n\u003e represented as a JSON array of certificate value strings.  Each\n\u003e string in the array is a base64 encoded ([RFC4648] Section 4 \u0026#x2013;\n\u003e not base64url encoded) DER [ITU.X690.1994] PKIX certificate value.\n\u003e The PKIX certificate containing the key value MUST be the first\n\u003e certificate.  This MAY be followed by additional certificates,\n\u003e with each subsequent certificate being the one used to certify the\n\u003e previous one.  The key in the first certificate MUST match the\n\u003e public key represented by other members of the JWK.  Use of this\n\u003e member is OPTIONAL.\n\u003e \n\u003e As with the \"x5u\" member, members other than those representing\n\u003e the public key may also be populated when an \"x5c\" member is\n\u003e present.  If other members are present, the contents of those\n\u003e members MUST be semantically consistent with the related fields in\n\u003e the first certificate.  See the last paragraph of Section 4.6 for\n\u003e additional guidance on this.\n\nFinally, here is what the function looks like when it's all put\ntogether, with additional error handling code:\n\n    # FIXME: Rename since this is not about public keys anymore\n    def fetch_jwt_public_key_for(id_token=None):\n        if id_token is None:\n            raise NameError('id_token is required')\n    \n        dirty_header = jws.get_unverified_header(id_token)\n        cleaned_key_id = None\n        if 'kid' in dirty_header:\n            dirty_key_id = dirty_header['kid']\n            cleaned_key_id = re.sub(not_alpha_numeric, '', dirty_key_id)\n        else:\n            raise ValueError('The id_token header must contain a \"kid\"')\n        if cleaned_key_id in public_keys:\n            return public_keys[cleaned_key_id]\n    \n        unverified_claims = jwt.get_unverified_claims(id_token)\n        dirty_url = urlparse.urlparse(unverified_claims['iss'])\n        if domain_name_for(dirty_url) not in allowed_domains:\n            raise ValueError('The domain in the issuer claim is not allowed')\n        cleaned_issuer = dirty_url.geturl()\n        oidc_discovery_url = \"{}/.well-known/openid-configuration\".format(\n            cleaned_issuer)\n        r = requests.get(oidc_discovery_url)\n        openid_configuration = r.json()\n        jwks_uri = openid_configuration['jwks_uri']\n        r = requests.get(jwks_uri)\n        jwks = r.json()\n        for key in jwks['keys']:\n            jwk_id = key['kid']\n            public_keys[jwk_id] = key\n    \n        if cleaned_key_id in public_keys:\n            return public_keys[cleaned_key_id]\n        else:\n            raise RuntimeError(\"Unable to fetch public key from jwks_uri\")\n\n## Single Page App\n\nThis use case demonstrates how to have a Single Page application\nauthenticate users via OpenID Connect.\n\nThe code in this example is contained in two static files:\n`templates/spa.html` for the HTML and `static/single-page.js` for\nthe application JavaScript.\n\nThe JavaScript used to demonstrate this use case is covered below:\n\nWe start with the code used to initialize the Okta Sign-In Widget\nin the `spa.html` file, Note that the `{{okta.base_url}}` and\n`{{okta.client_id}}` strings are place holders for the [Jinja2](http://jinja.pocoo.org/)\ntemplating engine that Flask uses to render the `spa.html`\ntemplate.\n\n    var oktaSignIn = setupOktaSignIn('{{okta.base_url}}', '{{okta.client_id}}');\n    \n    $(document).ready(function () {\n        // defined in 'single-page.js'\n        renderOktaWidget();\n    });\n\nThe rest of the code used in this demonstration is contained in the\n`single-page.js` file. \n\nThis demonstration application is a very simplistic and\n*unrealistic* implementation of a Single Page Application. Instead\nof using a framework [Angular](https://angularjs.org/), [Ember](http://emberjs.com/), or [React](https://facebook.github.io/react/), this examples uses\n[jQuery](https://jquery.com/) to update the page.\n\n(Using jQuery is easier to understand, but you *should not* use jQuery\nto write a production quality Single Page Application.)\n\nThe `single-page.js` file defines three functions:\n\n-   `renderOktaWidget()`\n         This handles rendering of the Okta widget.\n-   `renderLogin()`\n    What gets called when a user logs in with a `status` of\n    \"`SUCCESS`\".\n-   `renderLogout()`\n         What gets called when a user clicks a \"Logout\" button or link.\n\nWe will cover each function below.\n\n### `renderOktaWidget()`\n\nBelow is the `renderOktaWidget()` function which calls the\n`renderEl` (\"render El\"ement) method of\n`oktaSignIn`. `renderEl` takes three arguments:\n\n1.  `widget-location-object`\n    A JavaScript object which contains the `id` of the HTML element\n    that should be turned into the Okta Sign-In Widget.\n2.  `widget-success-function` \n           A function that is called on successful authentications.\n3.  `widget-error-function`\n           A function that is called when error conditions are encountered.\n\nHere is what the `renderEl` function looks like at a high level:\n\n    function renderOktaWidget() {\n        oktaSignIn.renderEl(\n            { el: '#okta-sign-in-widget' },\n            function (res) {\n                if (res.status === 'SUCCESS') {\n                    console.log(res);\n                    var id_token = res.id_token || res.idToken;\n                    $.ajax({\n                        type: \"GET\",\n                        dataType: 'json',\n                        url: \"/users/me\",\n                        beforeSend: function(xhr) {\n                            xhr.setRequestHeader(\"Authorization\", \"Bearer \" + id_token);\n                        },\n                        success: function(data){\n                            renderLogin(data.user_id);\n                        }\n                    });\n                }\n            },\n            function (err) { console.log('Unexpected error authenticating user: %o', err); }\n        );\n    }\n\nLet's cover each of those sections in detail:\n\nBelow we pass `renderEl` \"`#okta-sign-in-widget`\", which is the\nHTML `id` for the `\u003cdiv\u003e` tag that we want to contain the Okta\nSign-In Widget.\n\n    { el: '#okta-sign-in-widget' }\n\nNext, we pass `renderEl` a function that makes an [Ajax](https://en.wikipedia.org/wiki/Ajax_(programming)) request to\n`/users/me`. This call passes the `id_token` in the\n`Authorization` header to validate the request. If everything\nworks as expected, then we call the `renderLogin()` function with\nthe user's Okta ID as a parameter.\n\n    function (res) {\n        if (res.status === 'SUCCESS') {\n            console.log(res);\n            var id_token = res.id_token || res.idToken;\n            $.ajax({\n                type: \"GET\",\n                dataType: 'json',\n                url: \"/users/me\",\n                beforeSend: function(xhr) {\n                    xhr.setRequestHeader(\"Authorization\", \"Bearer \" + id_token);\n                },\n                success: function(data){\n                    renderLogin(data.user_id);\n                }\n            });\n        }\n    }\n\nLastly, we pass `renderEl` an error handling function. In this\nexample, we pass in a very simple error handling function that\njust calls `console.log()` with the error message. This is only\nuseful while developing your custom logic for the Okta Sign-In\nWidget and you will want to do something different in a production\ndeployment.\n\n    function (err) { console.log('Unexpected error authenticating user: %o', err); }\n\n### `renderLogin()`\n\nBelow is an overview of what the `renderLogin()` function\ndoes:\n\n    function renderLogin(user_id) {\n        $('#navbar \u003e ul').empty().append('\u003cli\u003e\u003ca id=\"logout\" href=\"/logout\"\u003eLog out\u003c/a\u003e\u003c/li\u003e');\n        $('#logout').click(function(event) {\n            event.preventDefault();\n            renderLogout();\n        });\n        $('#logged-out-message').hide();\n        $('#logged-in-message').show();\n    \n        $('#okta-sign-in-widget').hide();\n        $('#okta-user-id').empty().append(user_id);\n        $('#logged-in-user-id').show();\n    }\n\nHere is what each of the sections above do:\n\nFirst, we add a \"Log out\" item\nto the navbar, then register a `click()` event for when the user\nclicks on \"Log out\":\n\n    $('#navbar \u003e ul').empty().append('\u003cli\u003e\u003ca id=\"logout\" href=\"/logout\"\u003eLog out\u003c/a\u003e\u003c/li\u003e');\n    $('#logout').click(function(event) {\n        event.preventDefault();\n        renderLogout();\n    });\n\nNext, we hide the \"logged out\" message and display the \"logged in\" message:\n\n    $('#logged-out-message').hide();\n    $('#logged-in-message').show();\n\nLastly, in the `\u003c\u003cdisplay-user-id\u003e\u003e` section, we hide the Okta Sign-In\nWidget append the user's Okta ID into page, then show the part of\nthe page with the user's Okta ID:\n\n    $('#okta-sign-in-widget').hide();\n    $('#okta-user-id').empty().append(user_id);\n    $('#logged-in-user-id').show();\n\n**Note:** The `#okta-sign-in-widget` element can only be\ninstantiated once per page, so for a Single Page Application, it\nis critical that you hide the element instead of removing it.\n\nConvert your code to show and hide the `#okta-sign-in-widget`\nelement if your browser's JavaScript console shows an error that says:\n\"Backbone.history has already been started\" \n\n### `renderLogout()`\n\nThe `renderLogout()` function is essentially the opposite of the\n`renderLogin()`, it clears out the navigation bar with `empty`,\nhides the \"logged in\" message and shows the \"logged out\" message,\nhides the users Okta ID and shows the Okta Sign-In Widget. (This code\nalso clears out the password field in the sign-in widget).\n\n    function renderLogout() {\n        $('#navbar \u003e ul').empty();\n        $('#logged-in-message').hide();\n        $('#logged-out-message').show();\n        $('#logged-in-user-id').hide();\n        $('#okta-sign-in .okta-form-input-field input[type=\"password\"]').val('');\n        $('#okta-sign-in-widget').show();\n    }\n\n# Learn more\n\nWant to learn more about Open ID Connect and OAuth?\n\nHere is what we suggest that you read to learn more:\n\n-   Aaron Parecki's \"[OAuth 2 Simplified](https://aaronparecki.com/articles/2012/07/29/1/oauth2-simplified)\" post.\n    \n    Start here if you don't know anything about OAuth 2.\n-   Karl McGuinness' \"[Demystifying OAuth](http://developer.okta.com/blog/2015/12/07/oauth/)\" video and slides.\n    \n    This is a great high level guide that covers the basics of OAuth.\n-   [OpenID Connect Implicit Client Implementer's Guide](http://openid.net/specs/openid-connect-implicit-1_0.html)\n    \n    An official guide for implementing the \"implicit\" flow. Language\n    agnostic and very useful for learning the details on how things\n    work.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjpf%2Fokta-oidc-beta","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjpf%2Fokta-oidc-beta","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjpf%2Fokta-oidc-beta/lists"}