From 2601ab09b35488cea8d7d4dbe0448c3a7c356143 Mon Sep 17 00:00:00 2001 From: emorozov Date: Fri, 18 Nov 2022 20:30:53 +0600 Subject: [PATCH 1/7] initial commit From a4568c4e752da8ab88d8f500da7735c21f51af80 Mon Sep 17 00:00:00 2001 From: emorozov Date: Fri, 18 Nov 2022 20:32:10 +0600 Subject: [PATCH 2/7] initial commit From b0e552cf0159940ae07e459819281758b628962c Mon Sep 17 00:00:00 2001 From: emorozov Date: Fri, 18 Nov 2022 20:35:17 +0600 Subject: [PATCH 3/7] fixed imports --- docs/user/cards.rst | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/docs/user/cards.rst b/docs/user/cards.rst index 957f2db..b303cb0 100644 --- a/docs/user/cards.rst +++ b/docs/user/cards.rst @@ -4,23 +4,26 @@ Cards and Buttons ================= -Webex Teams supports `AdaptiveCards `_ to allow +Webex supports `AdaptiveCards `_ to allow new levels of interactivity for bots and integrations. You can read more about how cards and buttons work `in the official guide `_. -In this guide I want to cover the abstraction build into the webexteamssdk that +In this guide I want to cover the abstraction built into the webexteamssdk that lets you author adaptive cards in pure python without having to touch the -underlying json of a adaptive card. +underlying JSON of an adaptive card. + +Sending a card +============== Lets dive into a simple example that sends a card to a room .. code-block:: python from webexteamssdk import WebexTeamsAPI - from webexteamssdk.cards.card import AdaptiveCard - from webexteamssdk.cards.inputs import Text, Number - from webexteamssdk.cards.components import TextBlock - from webexteamssdk.cards.actions import Submit + from webexteamssdk.models.cards.card import AdaptiveCard + from webexteamssdk.models.cards.inputs import Text, Number + from webexteamssdk.models.cards.components import TextBlock + from webexteamssdk.models.cards.actions import Submit greeting = TextBlock("Hey hello there! I am a adaptive card") first_name = Text('first_name', placeholder="First Name") @@ -33,7 +36,7 @@ Lets dive into a simple example that sends a card to a room api = WebexTeamsAPI() api.messages.create(text="fallback", roomId="...", attachments=[card]) -The message we send with this code then looks like this in our Webex Teams +The message we send with this code then looks like this in our Webex space client: .. image:: ../images/cards_sample.png From 775431425417bf1ebbf3233f4e0f2c671ce7b7b9 Mon Sep 17 00:00:00 2001 From: emorozov Date: Fri, 18 Nov 2022 20:35:53 +0600 Subject: [PATCH 4/7] added adaptive card processing sample --- docs/user/cards.rst | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/docs/user/cards.rst b/docs/user/cards.rst index b303cb0..75a6c66 100644 --- a/docs/user/cards.rst +++ b/docs/user/cards.rst @@ -40,3 +40,36 @@ The message we send with this code then looks like this in our Webex space client: .. image:: ../images/cards_sample.png + + +Processing a card action +======================= + +Adaptive card interactions are treated as "attachment actions". Once user interacts +with your card and submits an action, your app will receive a webhook from Webex. You +must `setup a webhook `_ in advance with ``resource = "attachmentActions"`` +and ``event = "created"``. + +Webhook payload will contain a JSON: + +.. code-block:: json + + { + "resource": "attachmentActions", + "event": "created", + "data": { + "id": "XYXYXY", + "type": "submit" + } + } + +Extract attachment action ID from ``['data']['id']`` and +use `attachment_actions.get() `_ to get full information +about user action and any submitted data. + +.. code-block:: python + + action = api.attachment_actions.get(webhookJson['data']['id']) + + first_name = action.inputs['first_name'] + age = action.inputs['age'] From fb6d7114af28c8f6c1b6c334cba10cf5e64a60d2 Mon Sep 17 00:00:00 2001 From: emorozov Date: Mon, 21 Nov 2022 15:39:06 +0600 Subject: [PATCH 5/7] initial commit --- examples/oauth-flask-ngrok.py | 196 ++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 examples/oauth-flask-ngrok.py diff --git a/examples/oauth-flask-ngrok.py b/examples/oauth-flask-ngrok.py new file mode 100644 index 0000000..7ced48c --- /dev/null +++ b/examples/oauth-flask-ngrok.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +"""Implementation of OAuth for Webex Integration with Flask and ngrok. + +This sample script leverages the Flask web service micro-framework (see +https://flask.palletsprojects.com/). + +Ngrok (https://ngrok.com/) can be used to tunnel traffic back to your server if +your machine sits behind a firewall. Free account registration required. Ngrok +is launched with "./ngrok http 5000" + +You must create a Webex Integration in the My Webex Apps section of +https://developer.webex.com. For details, see +https://developer.webex.com/docs/integrations . Copy your integration Client ID, +Client Secret, scopes and set Redirect URI as "http://localhost:5000/callback" +or the public ngrok URI + "/callback". + +Copyright (c) 2022 Cisco and/or its affiliates. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + + +from flask import Flask, url_for, session, redirect, request +import urllib.parse +from uuid import uuid4 +import requests + +from webexteamssdk import WebexTeamsAPI + +# Parameters configured in Webex Integration +OAUTH_CLIENT_ID = "your integration client ID" +OAUTH_CLIENT_SECRET = "your integration Client Secret" +OAUTH_CALLBACK_URI = "http://localhost:5000/callback" +# Scopes are space-separated. Can use any subset of the configured scopes. +OAUTH_SCOPE = "spark:people_read meeting:schedules_read" + +# Static Webex URIs +oauth_authorizationUri = "https://webexapis.com/v1/authorize?" +oauth_tokenUri = "https://webexapis.com/v1/access_token" + + +# Get the ngrok public URI. It can be used instead of the localhost URI, as long +# as it is also configured in the Webex integration +r = requests.get("http://localhost:4040/api/tunnels") +public_url = r.json()['tunnels'][0]['public_url'] +# OAUTH_CALLBACK_URI = public_url + "/callback" + +# Create Flask app instance +app = Flask(__name__) + +# Flask secret key is required to use session +app.secret_key = "very bad secret" + + +# Welcome page. Link to auth from this page. +@app.route("/") +def root(): + print("/ requested") + return (""" +

Hey, this is Flask!

+

Click here to authenticate Webex integration.

+ """.format(url_for("auth"))) + + +# OAuth Step 1 - Build authorization URL and redirect user. +# Redirect the user/resource owner to the OAuth provider using an URL with a few +# key OAuth parameters. +@app.route("/auth") +def auth(): + print("Authorization requested") + + # State is used to prevent CSRF, generate random and save in session. + # Not mandatory but improves security. + oauth_state = str(uuid4()) + session['oauth_state'] = oauth_state + + # All parameters we need to pass to authorization service + oauth_params = { + 'response_type': "code", + 'client_id': OAUTH_CLIENT_ID, + 'redirect_uri': OAUTH_CALLBACK_URI, + 'scope': OAUTH_SCOPE, + 'state': oauth_state + } + authorizationUri = oauth_authorizationUri + urllib.parse.urlencode(oauth_params) + + return redirect(authorizationUri) + + +# OAuth Step 2 - User authorization. +# This happens on the provider side. + + +# OAuth Step 3 - Receive authoriation code and obtain access token. +# The user has been redirected back from the provider to your registered +# callback URL. With this redirection comes an authorization code included +# in the redirect URL. We will use that code to obtain an access token. +# The access token can be then used for any API calls within the authorized scopes. +@app.route("/callback", methods=["GET"]) +def callback(): + print("OAuth callback received") + + oauth_error = request.args.get("error_description", '') + if oauth_error: + return "OAuth error: " + oauth_error + + oauth_code = request.args.get("code") + if not oauth_code: + return "OAuth error: Authorization provider did not return authorization code." + + # check state to prevent CSRF + oauth_state = request.args.get('state', '') + if not oauth_state: + return "OAuth error: Authorization provider did not return state." + if oauth_state != session['oauth_state']: + return "OAuth error: State does not match." + + + # There are three options how the OAuth authorization code can be used + + # 1. + # The API connection can be directly initialized with OAuth information. It + # will exchange the OAuth authorization code to access token behind the + # scenes. It is the easiest, but the drawback is the refresh token is lost + # and cannot be saved. + api = WebexTeamsAPI( + client_id=OAUTH_CLIENT_ID, + client_secret=OAUTH_CLIENT_SECRET, + oauth_code=oauth_code, + redirect_uri=OAUTH_CALLBACK_URI + ) + print(api.people.me()) + return "Welcome, {}!".format(api.people.me().displayName) + + + # 2. + # The API connection object can be initialized with any string. Then use the + # access_tokens endpoint to obtain access and refresh tokens. The access + # token is valid for 14 days. The refresh token may be saved somewhere and + # later used to refresh the access token with access_tokens.refresh() + # api = WebexTeamsAPI("any string") + # access_tokens = api.access_tokens.get( + # client_id=OAUTH_CLIENT_ID, + # client_secret=OAUTH_CLIENT_SECRET, + # code=oauth_code, + # redirect_uri=OAUTH_CALLBACK_URI + # ) + # access_token = access_tokens.access_token + # refresh_token = access_tokens.refresh_token + # # Reinit API connection with the obtained access_token + # api.__init__(access_token) + # print(api.people.me()) + # return "Welcome, {}!".format(api.people.me().displayName) + + + # 3. + # Alternatively, can use requests to exchange authorization code to access + # token without usung the Webex SDK. Useful if your authorization process is + # separate from the token usage. Save tokens somewhere for later use. + # oauth_data = { + # 'grant_type': "authorization_code", + # 'redirect_uri': OAUTH_CALLBACK_URI, + # 'code': oauth_code, + # 'client_id': OAUTH_CLIENT_ID, + # 'client_secret': OAUTH_CLIENT_SECRET + # } + # resp = requests.post(oauth_tokenUri, data=oauth_data) + # if not resp.ok: + # return "OAuth error: Authorization provider returned:
{}".format(resp.json()['error_description']) + # else: + # oauth_tokens = resp.json() + # access_token = oauth_tokens["access_token"] + # refresh_token = oauth_tokens["refresh_token"] + # # save tokens + # return "Authorization successful." + + +if __name__ == "__main__": + # Start Flask web server + app.run(port=5000) From 51a70dc17d6f5b29831b69d99dc18fd606b986cd Mon Sep 17 00:00:00 2001 From: emorozov Date: Mon, 21 Nov 2022 16:03:00 +0600 Subject: [PATCH 6/7] remove last commit to another branch --- examples/oauth-flask-ngrok.py | 196 ++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 examples/oauth-flask-ngrok.py diff --git a/examples/oauth-flask-ngrok.py b/examples/oauth-flask-ngrok.py new file mode 100644 index 0000000..7ced48c --- /dev/null +++ b/examples/oauth-flask-ngrok.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +"""Implementation of OAuth for Webex Integration with Flask and ngrok. + +This sample script leverages the Flask web service micro-framework (see +https://flask.palletsprojects.com/). + +Ngrok (https://ngrok.com/) can be used to tunnel traffic back to your server if +your machine sits behind a firewall. Free account registration required. Ngrok +is launched with "./ngrok http 5000" + +You must create a Webex Integration in the My Webex Apps section of +https://developer.webex.com. For details, see +https://developer.webex.com/docs/integrations . Copy your integration Client ID, +Client Secret, scopes and set Redirect URI as "http://localhost:5000/callback" +or the public ngrok URI + "/callback". + +Copyright (c) 2022 Cisco and/or its affiliates. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + + +from flask import Flask, url_for, session, redirect, request +import urllib.parse +from uuid import uuid4 +import requests + +from webexteamssdk import WebexTeamsAPI + +# Parameters configured in Webex Integration +OAUTH_CLIENT_ID = "your integration client ID" +OAUTH_CLIENT_SECRET = "your integration Client Secret" +OAUTH_CALLBACK_URI = "http://localhost:5000/callback" +# Scopes are space-separated. Can use any subset of the configured scopes. +OAUTH_SCOPE = "spark:people_read meeting:schedules_read" + +# Static Webex URIs +oauth_authorizationUri = "https://webexapis.com/v1/authorize?" +oauth_tokenUri = "https://webexapis.com/v1/access_token" + + +# Get the ngrok public URI. It can be used instead of the localhost URI, as long +# as it is also configured in the Webex integration +r = requests.get("http://localhost:4040/api/tunnels") +public_url = r.json()['tunnels'][0]['public_url'] +# OAUTH_CALLBACK_URI = public_url + "/callback" + +# Create Flask app instance +app = Flask(__name__) + +# Flask secret key is required to use session +app.secret_key = "very bad secret" + + +# Welcome page. Link to auth from this page. +@app.route("/") +def root(): + print("/ requested") + return (""" +

Hey, this is Flask!

+

Click here to authenticate Webex integration.

+ """.format(url_for("auth"))) + + +# OAuth Step 1 - Build authorization URL and redirect user. +# Redirect the user/resource owner to the OAuth provider using an URL with a few +# key OAuth parameters. +@app.route("/auth") +def auth(): + print("Authorization requested") + + # State is used to prevent CSRF, generate random and save in session. + # Not mandatory but improves security. + oauth_state = str(uuid4()) + session['oauth_state'] = oauth_state + + # All parameters we need to pass to authorization service + oauth_params = { + 'response_type': "code", + 'client_id': OAUTH_CLIENT_ID, + 'redirect_uri': OAUTH_CALLBACK_URI, + 'scope': OAUTH_SCOPE, + 'state': oauth_state + } + authorizationUri = oauth_authorizationUri + urllib.parse.urlencode(oauth_params) + + return redirect(authorizationUri) + + +# OAuth Step 2 - User authorization. +# This happens on the provider side. + + +# OAuth Step 3 - Receive authoriation code and obtain access token. +# The user has been redirected back from the provider to your registered +# callback URL. With this redirection comes an authorization code included +# in the redirect URL. We will use that code to obtain an access token. +# The access token can be then used for any API calls within the authorized scopes. +@app.route("/callback", methods=["GET"]) +def callback(): + print("OAuth callback received") + + oauth_error = request.args.get("error_description", '') + if oauth_error: + return "OAuth error: " + oauth_error + + oauth_code = request.args.get("code") + if not oauth_code: + return "OAuth error: Authorization provider did not return authorization code." + + # check state to prevent CSRF + oauth_state = request.args.get('state', '') + if not oauth_state: + return "OAuth error: Authorization provider did not return state." + if oauth_state != session['oauth_state']: + return "OAuth error: State does not match." + + + # There are three options how the OAuth authorization code can be used + + # 1. + # The API connection can be directly initialized with OAuth information. It + # will exchange the OAuth authorization code to access token behind the + # scenes. It is the easiest, but the drawback is the refresh token is lost + # and cannot be saved. + api = WebexTeamsAPI( + client_id=OAUTH_CLIENT_ID, + client_secret=OAUTH_CLIENT_SECRET, + oauth_code=oauth_code, + redirect_uri=OAUTH_CALLBACK_URI + ) + print(api.people.me()) + return "Welcome, {}!".format(api.people.me().displayName) + + + # 2. + # The API connection object can be initialized with any string. Then use the + # access_tokens endpoint to obtain access and refresh tokens. The access + # token is valid for 14 days. The refresh token may be saved somewhere and + # later used to refresh the access token with access_tokens.refresh() + # api = WebexTeamsAPI("any string") + # access_tokens = api.access_tokens.get( + # client_id=OAUTH_CLIENT_ID, + # client_secret=OAUTH_CLIENT_SECRET, + # code=oauth_code, + # redirect_uri=OAUTH_CALLBACK_URI + # ) + # access_token = access_tokens.access_token + # refresh_token = access_tokens.refresh_token + # # Reinit API connection with the obtained access_token + # api.__init__(access_token) + # print(api.people.me()) + # return "Welcome, {}!".format(api.people.me().displayName) + + + # 3. + # Alternatively, can use requests to exchange authorization code to access + # token without usung the Webex SDK. Useful if your authorization process is + # separate from the token usage. Save tokens somewhere for later use. + # oauth_data = { + # 'grant_type': "authorization_code", + # 'redirect_uri': OAUTH_CALLBACK_URI, + # 'code': oauth_code, + # 'client_id': OAUTH_CLIENT_ID, + # 'client_secret': OAUTH_CLIENT_SECRET + # } + # resp = requests.post(oauth_tokenUri, data=oauth_data) + # if not resp.ok: + # return "OAuth error: Authorization provider returned:
{}".format(resp.json()['error_description']) + # else: + # oauth_tokens = resp.json() + # access_token = oauth_tokens["access_token"] + # refresh_token = oauth_tokens["refresh_token"] + # # save tokens + # return "Authorization successful." + + +if __name__ == "__main__": + # Start Flask web server + app.run(port=5000) From 217dda3b4884d50ab0b03eaf095f6b4f8120adf3 Mon Sep 17 00:00:00 2001 From: emorozov Date: Mon, 21 Nov 2022 16:08:10 +0600 Subject: [PATCH 7/7] remove last commit to another branch --- examples/oauth-flask-ngrok.py | 196 ---------------------------------- 1 file changed, 196 deletions(-) delete mode 100644 examples/oauth-flask-ngrok.py diff --git a/examples/oauth-flask-ngrok.py b/examples/oauth-flask-ngrok.py deleted file mode 100644 index 7ced48c..0000000 --- a/examples/oauth-flask-ngrok.py +++ /dev/null @@ -1,196 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -"""Implementation of OAuth for Webex Integration with Flask and ngrok. - -This sample script leverages the Flask web service micro-framework (see -https://flask.palletsprojects.com/). - -Ngrok (https://ngrok.com/) can be used to tunnel traffic back to your server if -your machine sits behind a firewall. Free account registration required. Ngrok -is launched with "./ngrok http 5000" - -You must create a Webex Integration in the My Webex Apps section of -https://developer.webex.com. For details, see -https://developer.webex.com/docs/integrations . Copy your integration Client ID, -Client Secret, scopes and set Redirect URI as "http://localhost:5000/callback" -or the public ngrok URI + "/callback". - -Copyright (c) 2022 Cisco and/or its affiliates. - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -""" - - -from flask import Flask, url_for, session, redirect, request -import urllib.parse -from uuid import uuid4 -import requests - -from webexteamssdk import WebexTeamsAPI - -# Parameters configured in Webex Integration -OAUTH_CLIENT_ID = "your integration client ID" -OAUTH_CLIENT_SECRET = "your integration Client Secret" -OAUTH_CALLBACK_URI = "http://localhost:5000/callback" -# Scopes are space-separated. Can use any subset of the configured scopes. -OAUTH_SCOPE = "spark:people_read meeting:schedules_read" - -# Static Webex URIs -oauth_authorizationUri = "https://webexapis.com/v1/authorize?" -oauth_tokenUri = "https://webexapis.com/v1/access_token" - - -# Get the ngrok public URI. It can be used instead of the localhost URI, as long -# as it is also configured in the Webex integration -r = requests.get("http://localhost:4040/api/tunnels") -public_url = r.json()['tunnels'][0]['public_url'] -# OAUTH_CALLBACK_URI = public_url + "/callback" - -# Create Flask app instance -app = Flask(__name__) - -# Flask secret key is required to use session -app.secret_key = "very bad secret" - - -# Welcome page. Link to auth from this page. -@app.route("/") -def root(): - print("/ requested") - return (""" -

Hey, this is Flask!

-

Click here to authenticate Webex integration.

- """.format(url_for("auth"))) - - -# OAuth Step 1 - Build authorization URL and redirect user. -# Redirect the user/resource owner to the OAuth provider using an URL with a few -# key OAuth parameters. -@app.route("/auth") -def auth(): - print("Authorization requested") - - # State is used to prevent CSRF, generate random and save in session. - # Not mandatory but improves security. - oauth_state = str(uuid4()) - session['oauth_state'] = oauth_state - - # All parameters we need to pass to authorization service - oauth_params = { - 'response_type': "code", - 'client_id': OAUTH_CLIENT_ID, - 'redirect_uri': OAUTH_CALLBACK_URI, - 'scope': OAUTH_SCOPE, - 'state': oauth_state - } - authorizationUri = oauth_authorizationUri + urllib.parse.urlencode(oauth_params) - - return redirect(authorizationUri) - - -# OAuth Step 2 - User authorization. -# This happens on the provider side. - - -# OAuth Step 3 - Receive authoriation code and obtain access token. -# The user has been redirected back from the provider to your registered -# callback URL. With this redirection comes an authorization code included -# in the redirect URL. We will use that code to obtain an access token. -# The access token can be then used for any API calls within the authorized scopes. -@app.route("/callback", methods=["GET"]) -def callback(): - print("OAuth callback received") - - oauth_error = request.args.get("error_description", '') - if oauth_error: - return "OAuth error: " + oauth_error - - oauth_code = request.args.get("code") - if not oauth_code: - return "OAuth error: Authorization provider did not return authorization code." - - # check state to prevent CSRF - oauth_state = request.args.get('state', '') - if not oauth_state: - return "OAuth error: Authorization provider did not return state." - if oauth_state != session['oauth_state']: - return "OAuth error: State does not match." - - - # There are three options how the OAuth authorization code can be used - - # 1. - # The API connection can be directly initialized with OAuth information. It - # will exchange the OAuth authorization code to access token behind the - # scenes. It is the easiest, but the drawback is the refresh token is lost - # and cannot be saved. - api = WebexTeamsAPI( - client_id=OAUTH_CLIENT_ID, - client_secret=OAUTH_CLIENT_SECRET, - oauth_code=oauth_code, - redirect_uri=OAUTH_CALLBACK_URI - ) - print(api.people.me()) - return "Welcome, {}!".format(api.people.me().displayName) - - - # 2. - # The API connection object can be initialized with any string. Then use the - # access_tokens endpoint to obtain access and refresh tokens. The access - # token is valid for 14 days. The refresh token may be saved somewhere and - # later used to refresh the access token with access_tokens.refresh() - # api = WebexTeamsAPI("any string") - # access_tokens = api.access_tokens.get( - # client_id=OAUTH_CLIENT_ID, - # client_secret=OAUTH_CLIENT_SECRET, - # code=oauth_code, - # redirect_uri=OAUTH_CALLBACK_URI - # ) - # access_token = access_tokens.access_token - # refresh_token = access_tokens.refresh_token - # # Reinit API connection with the obtained access_token - # api.__init__(access_token) - # print(api.people.me()) - # return "Welcome, {}!".format(api.people.me().displayName) - - - # 3. - # Alternatively, can use requests to exchange authorization code to access - # token without usung the Webex SDK. Useful if your authorization process is - # separate from the token usage. Save tokens somewhere for later use. - # oauth_data = { - # 'grant_type': "authorization_code", - # 'redirect_uri': OAUTH_CALLBACK_URI, - # 'code': oauth_code, - # 'client_id': OAUTH_CLIENT_ID, - # 'client_secret': OAUTH_CLIENT_SECRET - # } - # resp = requests.post(oauth_tokenUri, data=oauth_data) - # if not resp.ok: - # return "OAuth error: Authorization provider returned:
{}".format(resp.json()['error_description']) - # else: - # oauth_tokens = resp.json() - # access_token = oauth_tokens["access_token"] - # refresh_token = oauth_tokens["refresh_token"] - # # save tokens - # return "Authorization successful." - - -if __name__ == "__main__": - # Start Flask web server - app.run(port=5000)