diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 0c055b85..46bba864 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -43,6 +43,8 @@ jobs: AUTH0_PASSWORD: ${{ secrets.AUTH0_PASSWORD }} AUTH0_PKCE_CLIENT_ID: ${{ secrets.AUTH0_PKCE_CLIENT_ID }} AUTH0_DOMAIN: ${{ secrets.AUTH0_DOMAIN }} + LINKEDIN_CLIENT_ID: ${{ secrets.LINKEDIN_CLIENT_ID }} + LINKEDIN_SECRET: ${{ secrets.LINKEDIN_SECRET }} TOXENV: ${{ matrix.tox-env }} run: | tox diff --git a/docs/examples/linkedin.rst b/docs/examples/linkedin.rst deleted file mode 100644 index 71dd7331..00000000 --- a/docs/examples/linkedin.rst +++ /dev/null @@ -1,49 +0,0 @@ -LinkedIn OAuth 2 Tutorial -========================= - -Setup credentials following the instructions on `LinkedIn`_. When you -have obtained a ``client_id`` and a ``client_secret`` you can try out the -command line interactive example below. - -.. _`LinkedIn`: https://www.linkedin.com/secure/developer - -.. code-block:: pycon - - >>> # Imports - >>> import os - >>> from requests_oauthlib import OAuth2Session - - >>> # Set environment variables - >>> os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' - - >>> # Credentials you get from registering a new application - >>> client_id = '' - >>> client_secret = '' - - >>> # LinkedIn OAuth2 requests require scope and redirect_url parameters. - >>> # Ensure these values match the auth values in your LinkedIn App - >>> # (see auth tab on LinkedIn Developer page) - >>> scope = ['r_liteprofile'] - >>> redirect_url = 'http://127.0.0.1' - - >>> # OAuth endpoints given in the LinkedIn API documentation - >>> authorization_base_url = 'https://www.linkedin.com/oauth/v2/authorization' - >>> token_url = 'https://www.linkedin.com/oauth/v2/accessToken' - - >>> linkedin = OAuth2Session(client_id, redirect_uri='http://127.0.0.1', scope=scope) - - >>> # Redirect user to LinkedIn for authorization - >>> authorization_url, state = linkedin.authorization_url(authorization_base_url) - >>> print(f"Please go here and authorize: {authorization_url}") - - >>> # Get the authorization verifier code from the callback url - >>> redirect_response = input('Paste the full redirect URL here:') - - >>> # Fetch the access token - >>> linkedin.fetch_token(token_url, client_secret=client_secret, - ... include_client_id=True, - ... authorization_response=redirect_response) - - >>> # Fetch a protected resource, i.e. user profile - >>> r = linkedin.get('https://api.linkedin.com/v2/me') - >>> print(r.content) diff --git a/docs/examples/linkedin_OAuth2_example.py b/docs/examples/linkedin_OAuth2_example.py new file mode 100644 index 00000000..531170e9 --- /dev/null +++ b/docs/examples/linkedin_OAuth2_example.py @@ -0,0 +1,38 @@ +# Imports +import os +from requests_oauthlib import OAuth2Session + +# Set environment variables +os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' + +# Credentials you get from registering a new application +client_id = '' +client_secret = '' + +# LinkedIn OAuth2 requests require scope and redirect_url parameters. +# Ensure these values match the auth values in your LinkedIn App +# (see auth tab on LinkedIn Developer page) +scope = ['r_liteprofile'] +redirect_url = 'http://127.0.0.1' + +# OAuth endpoints given in the LinkedIn API documentation +authorization_base_url = 'https://www.linkedin.com/oauth/v2/authorization' +token_url = 'https://www.linkedin.com/oauth/v2/accessToken' + +linkedin = OAuth2Session(client_id, redirect_uri='http://127.0.0.1', scope=scope) + +# Redirect user to LinkedIn for authorization +authorization_url, state = linkedin.authorization_url(authorization_base_url) +print(f"Please go here and authorize: {authorization_url}") + +# Get the authorization verifier code from the callback url +redirect_response = input('Paste the full redirect URL here:') + +# Fetch the access token +linkedin.fetch_token(token_url, client_secret=client_secret, + include_client_id=True, + authorization_response=redirect_response) + +# Fetch a protected resource, i.e. user profile +r = linkedin.get('https://api.linkedin.com/v2/me') +print(r.content) diff --git a/docs/examples/linkedin_OAuth2_example.rst b/docs/examples/linkedin_OAuth2_example.rst new file mode 100644 index 00000000..5ee8e098 --- /dev/null +++ b/docs/examples/linkedin_OAuth2_example.rst @@ -0,0 +1,37 @@ +LinkedIn OAuth 2.0 Tutorial +=========================== + +Overview +-------- +This tutorial guides you through the process of setting up OAuth 2.0 authentication with LinkedIn. You will learn how to register your application with LinkedIn, obtain necessary credentials, and implement the OAuth 2.0 flow to authenticate users and access LinkedIn's API. + +Prerequisites +------------- +Before you begin, you need to have a LinkedIn developer account. Register your application with LinkedIn to obtain the ``client_id`` and ``client_secret`` needed for the OAuth process. You will also be able to learn about application scope which while it is not unqiue/sensitive information, will need to be set to successfully make a call to a protected resource/endpoint. + +Register Your Application +------------------------- +1. Visit the LinkedIn Developer portal. +2. Create and register an application with Linkedin, the resource and indentity provider. +3. Note down the ``client_id`` and ``client_secret`` provided after registration. + +Setting Up the OAuth Flow +------------------------- +This is a simple script demonstrating the workflow using this library in a cli environment. + +1. Ensure you have installed the ``requests_oauthlib`` package. If not, you can install it using pip: + + .. code-block:: bash + + pip install requests_oauthlib + +2. Update the cooresponding variable values with your ``client_id`` and ``client_secret`` obtained from LinkedIn. + +3. Update then run the script. Follow the instructions printed in the console. + +Python Script +------------- +Plug your ``client_id`` and a ``client_secret`` into the command line interactive example below. + +.. literalinclude:: linkedin_OAuth2_example.py + :language: python diff --git a/tests/examples/test_linkedin_example.py b/tests/examples/test_linkedin_example.py new file mode 100644 index 00000000..fa62185e --- /dev/null +++ b/tests/examples/test_linkedin_example.py @@ -0,0 +1,37 @@ +import os +import unittest +from . import Sample + +class TestLinkedInExample(Sample, unittest.TestCase): + def setUp(self): + super().setUp() + self.client_id = os.environ.get("LINKEDIN_CLIENT_ID") + self.client_secret = os.environ.get("LINKEDIN_CLIENT_SECRET") + os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' + + # Skip the test if the credentials are not setup + if not self.client_id or not self.client_secret: + self.skipTest("LinkedIn credentials are not configured properly") + + def test_linkedin_oauth_flow(self): + self.run_sample( + "linkedin_OAuth2_example.py", { + 'LINKEDIN_CLIENT_ID': self.client_id, + 'LINKEDIN_CLIENT_SECRET': self.client_secret, + } + ) + + # Wait for the script to output the authorization URL + authorize_url = self.wait_for_pattern("Please go here and authorize:") + print(f"Authorization URL provided: {authorize_url}") + + mock_redirect_response = 'http://127.0.0.1/?code=A_VALID_CODE_FROM_LINKEDIN' + # So if I am testing this examples application flow I mock everything from here on out??? + # Probably need integration/e2e testing to detect if the third part API contract is changed, breaking the example. + self.write(mock_redirect_response) + + # TODO: mock the token exchange + # TODO: mock the response from the protected endpoint api call + # TODO: add assertions +if __name__ == '__main__': + unittest.main()