OAuth Authentication with Flask

Posted by
on under

Many web sites offer users the option to use a streamlined single-click registration and login built on third party authentication services, typically run by the big social networks. In this article I want to give you an introduction to the OAuth protocol, which is one of the most used third-party authentication mechanism. I will also show you a complete Flask application that implements "Sign In with Facebook" and "Sign In with Twitter" functionality. With these two implementations as a guide you should find it easy to add any other OAuth providers you may need.

Brief Introduction to OAuth

The best way to explain OAuth is by going over the list of events that occur during the sign in process:

  1. The user navigates to the application's home page, say http://www.example.com, and clicks the "Sign in with Facebook" button, which links to an application route, for example http://www.example.com/authorize/facebook.
  2. The server receives the request and responds with a redirect to Facebook's OAuth authorization URL. All OAuth providers must document a URL to redirect the user to.
  3. The user is now prompted to login to Facebook (if not logged in already). Then a request to share information is presented, where the user needs to give Facebook permission to share the requested information with the originating application. This is all done at Facebook's website and is a private transaction between Facebook and the user, the application does not participate.
  4. Once the user accepts the request to share information, Facebook redirects back to the application at a pre-configured callback URL, for example http://www.example.com/callback/facebook. The query string of the redirect URL includes an authorization code that the application can use to access the Facebook API on behalf of the user.
  5. The application uses the Facebook API to obtain user information. Of particular interest is a unique identifier for the user, which can be used to register the user in the application's database, and once the user is registered to perform a login.

You can see above that the exchange between the application and the third party service is not trivial, but for the user it is extremely simple, since all the user needs to do is log in to the third party site and give permission to share information with the application.

There are two versions of the OAuth protocol currently in use, both following the overall process described above but with some implementation differences. OAuth 1.0a, used by Twitter, is the most complex of the two. OAuth 2, used by Facebook, is a backwards incompatible revision of the protocol that eliminates much of the complexity of version 1.0a by relying on secure HTTP for encryption.

Registration with OAuth Providers

Before an application can use a third party OAuth provider it needs to register with it. For Facebook and Twitter this is done on their respective developer sites with the creation of an "app" that represents the application for users of these sites.

To create a Facebook app you can visit https://developer.facebook.com. Select "Add a New App" from the Apps dropdown, and make the type "WWW/Website". Then enter a name and category for your app. Once the application is created, go to the "App Configuration" section and set the URL of the application, which in the case of you running it on your own computer will be http://localhost:5000.

For Twitter the location is https://apps.twitter.com. You will be asked to provide the app name, description, website and callback URL. For the last two you can enter placeholders, for example http://example.com and http://example.com/callback/twitter.

Note: If you want to use other OAuth providers you will need to find the appropriate procedure to register an application in their developer documentation.

The newly created app will be assigned two codes, usually called "id" and "secret". These identify the application that is making the authentication request, and are passed in the query string of the redirect URL to the provider site, in step 2 above.

OAuth Authentication Example

In the following sections I'm going to describe a relatively simple Flask application that implements Facebook and Twitter authentication.

I'm only going to show you the important parts of the application in the article, but the complete application is available on this GitHub repository: https://github.com/miguelgrinberg/flask-oauth-example. At the end of this article I show you the instructions on how to run it.

User Model

The users of the example application are stored in a SQLAlchemy database. The application uses the Flask-SQLAlchemy extension to work with the database, and the Flask-Login extension to keep track of logged in users.

from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin

db = SQLAlchemy(app)
lm = LoginManager(app)

class User(UserMixin, db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    social_id = db.Column(db.String(64), nullable=False, unique=True)
    nickname = db.Column(db.String(64), nullable=False)
    email = db.Column(db.String(64), nullable=True)

@lm.user_loader
def load_user(id):
    return User.query.get(int(id))

The database has a single table for the users. In addition to the id that is the primary key, the users table contains three columns:

  • social_id: a string that defines a unique identifier from the third party authentication service used to login.
  • nickname: a nickname for the user. Must be defined for all users, and does not need to be unique.
  • email: the email address of the user. This column is optional.

The model also inherits from UserMixin from Flask-Login, as that gives it the methods required by that extension. The user_loader callback function, also required by Flask-Login, loads a user by its primary key.

OAuth Implementation

There are several OAuth client packages for Python. For this example I have decided to use Rauth. But even when using an OAuth package, there are many aspects of the authentication against OAuth service providers that are left up to each provider to implement, which makes the task harder.

First of all, there are the two versions of the OAuth protocol, both widely used. But even among different providers using the same OAuth version, there are many details that are not part of the specification and need to be done according to the provider's own documentation.

For this reason I have decided to implement an abstraction layer on top of Rauth, so that the Flask application can be written generically. Below you can see a simple base class under which the provider specific implementations will be written:

class OAuthSignIn(object):
    providers = None

    def __init__(self, provider_name):
        self.provider_name = provider_name
        credentials = current_app.config['OAUTH_CREDENTIALS'][provider_name]
        self.consumer_id = credentials['id']
        self.consumer_secret = credentials['secret']

    def authorize(self):
        pass

    def callback(self):
        pass

    def get_callback_url(self):
        return url_for('oauth_callback', provider=self.provider_name,
                       _external=True)

    @classmethod
    def get_provider(self, provider_name):
        if self.providers is None:
            self.providers = {}
            for provider_class in self.__subclasses__():
                provider = provider_class()
                self.providers[provider.provider_name] = provider
        return self.providers[provider_name]

class FacebookSignIn(OAuthSignIn):
    pass

class TwitterSignIn(OAuthSignIn):
    pass

The OAuthSignIn base class defines the structure that the subclasses that implement each provider must follow. The constructor initializes the provider's name, and the application id and secret assigned by it, which are obtained from the configuration. Below you can see how the example application is configured (of course you will need to replace these codes with your own when you try this application):

app.config['OAUTH_CREDENTIALS'] = {
    'facebook': {
        'id': '470154729788964',
        'secret': '010cc08bd4f51e34f3f3e684fbdea8a7'
    },
    'twitter': {
        'id': '3RzWQclolxWZIMq5LJqzRZPTl',
        'secret': 'm9TEd58DSEtRrZHpz2EjrV9AhsBRxKMo8m3kuIZj3zLwzwIimt'
    }
}

At a high level there are two significant events supported by this class that are common to all OAuth providers:

  1. Initiation of the authentication process. For this the application needs to redirect to the provider's web site to let the user authenticate there. This is represented by the authorize() method.
  2. Once the authentication is completed the provider redirects back to the application. This is handled in the callback() method. Since the provider does not have direct access to the internal methods of the application, it will be redirecting to a URL that will call it. The URL that the provider needs to redirect to is returned by the get_callback_url() method and is built using the provider name, so that each provider gets its own dedicated route.

The get_provider() class method is used to lookup the correct OAuthSignIn instance given a provider name. This method uses introspection to find all the OAuthSignIn subclasses, and then saves an instance of each in a dictionary.

OAuth Authentication with Rauth

Rauth represent OAuth providers with an object of class OAuth1Service or OAuth2Service, depending on the version of the protocol that it uses. I create an object of this class in each provider's OAuthSignIn subclass. The implementations for Facebook and Twitter are shown below:

class FacebookSignIn(OAuthSignIn):
    def __init__(self):
        super(FacebookSignIn, self).__init__('facebook')
        self.service = OAuth2Service(
            name='facebook',
            client_id=self.consumer_id,
            client_secret=self.consumer_secret,
            authorize_url='https://graph.facebook.com/oauth/authorize',           
            access_token_url='https://graph.facebook.com/oauth/access_token',
            base_url='https://graph.facebook.com/'
        )

class TwitterSignIn(OAuthSignIn):
    def __init__(self):
        super(TwitterSignIn, self).__init__('twitter')
        self.service = OAuth1Service(
            name='twitter',
            consumer_key=self.consumer_id,
            consumer_secret=self.consumer_secret,
            request_token_url='https://api.twitter.com/oauth/request_token',
            authorize_url='https://api.twitter.com/oauth/authorize',
            access_token_url='https://api.twitter.com/oauth/access_token',
            base_url='https://api.twitter.com/1.1/'
        )

For Facebook, a provider that implements OAuth 2, the OAuth2Service class is used. The service object is initialized with the name of the service and several OAuth specific arguments. The client_id and client_secret arguments are the ones assigned to the application in Facebook's developer site. The authorize_url and access_token_url are URLs defined by Facebook for applications to connect to during the authentication process. Finally, the base_url sets the prefix URL for any Facebook API calls once the authentication is complete.

Twitter implements OAuth 1.0a, so class OAuth1Service is used instead. In OAuth 1.0a the id and secret codes are called consumer_key and consumer_secret, but are otherwise identical in functionality to the OAuth 2 counterparts. The OAuth 1 protocol requires providers to expose three URLs instead of two, there is an additional one called request_token_url. The name and base_url arguments are identical to the ones used in OAuth 2 services. I should note that Twitter offers two options for the authorize_url parameter. The URL shown above, https://api.twitter.com/oauth/authorize, is the most secure, as it will present the user with a screen in which they need to give permission to the app to access Twitter every time. Changing that URL to https://api.twitter.com/oauth/authenticate will make Twitter ask for permission just the first time, and then will silently allow access for as long as the user is logged in to Twitter.

Note that there is no standardization for the OAuth entry point URLs, OAuth providers define these as they like. To add a new OAuth provider you will need to obtain these URLs from the provider's documentation.

OAuth Authorization Phase

When the user clicks the "Login in with ..." link to initiate an OAuth authentication the following application route is invoked:

@app.route('/authorize/<provider>')
def oauth_authorize(provider):
    if not current_user.is_anonymous():
        return redirect(url_for('index'))
    oauth = OAuthSignIn.get_provider(provider)
    return oauth.authorize()

This route first ensures that the user is not logged in, and then simply obtains the OAuthSignIn subclass appropriate for the given provider, and invokes its authorize() method to initiate the process. The authorize() implementation for Facebook and Twitter is shown below:

class FacebookSignIn(OAuthSignIn):
    # ...
    def authorize(self):
        return redirect(self.service.get_authorize_url(
            scope='email',
            response_type='code',
            redirect_uri=self.get_callback_url())
        )

class TwitterSignIn(OAuthSignIn):
    # ...
    def authorize(self):
        request_token = self.service.get_request_token(
            params={'oauth_callback': self.get_callback_url()}
        )
        session['request_token'] = request_token
        return redirect(self.service.get_authorize_url(request_token[0]))

For OAuth 2 providers like Facebook the implementation simply issues a redirect to a URL generated by rauth's service object. The scope is provider specific, in this case I am asking that I want Facebook to provide the user's email. The response_type=code argument tells the OAuth provider that the application is a web application (there are other possible values for different authentication workflows). Finally, the redirect_uri argument is set to the application route that the provider needs to invoke after it completes the authentication.

OAuth 1.0a providers use a slightly more complicated process that involves obtaining a request token from the provider, which is a list of two items, the first of which is then used as an argument in the redirect. The entire request token is saved to the user session because it will be needed again in the callback.

OAuth Callback Phase

The OAuth provider redirects back to the application after the user authenticates and gives permission to share information. The route that handles this callback is shown below:

@app.route('/callback/<provider>')
def oauth_callback(provider):
    if not current_user.is_anonymous():
        return redirect(url_for('index'))
    oauth = OAuthSignIn.get_provider(provider)
    social_id, username, email = oauth.callback()
    if social_id is None:
        flash('Authentication failed.')
        return redirect(url_for('index'))
    user = User.query.filter_by(social_id=social_id).first()
    if not user:
        user = User(social_id=social_id, nickname=username, email=email)
        db.session.add(user)
        db.session.commit()
    login_user(user, True)
    return redirect(url_for('index'))

This route instantiates the OAuthSignIn provider class and invokes its callback() method. This method has the function to complete the authentication with the provider and obtain the user information. The return value is a tuple with three values, a unique id (called social_id to differentiate it from the id primary key), the user's nickname and the user's email. The id and the nickname are mandatory, but in this example application I made the email optional, since Twitter never shares that information with applications.

The user is searched in the database by the social_id field, and if not found, a new user is added to the database with the information obtained from the provider, effectively registering new users automatically. The user is then logged with the login_user() function of Flask-Login, and finally redirected to the home page.

The implementation of the callback() method for the Facebook and Twitter OAuth providers is shown below:

class FacebookSignIn(OAuthSignIn):
    # ...
    def callback(self):
        def decode_json(payload):
            return json.loads(payload.decode('utf-8'))

        if 'code' not in request.args:
            return None, None, None
        oauth_session = self.service.get_auth_session(
            data={'code': request.args['code'],
                  'grant_type': 'authorization_code',
                  'redirect_uri': self.get_callback_url()},
            decoder=decode_json
        )
        me = oauth_session.get('me').json()
        return (
            'facebook$' + me['id'],
            me.get('email').split('@')[0],  # Facebook does not provide
                                            # username, so the email's user
                                            # is used instead
            me.get('email')
        )

class TwitterSignIn(OAuthSignIn):
    # ...
    def callback(self):
        request_token = session.pop('request_token')
        if 'oauth_verifier' not in request.args:
            return None, None, None
        oauth_session = self.service.get_auth_session(
            request_token[0],
            request_token[1],
            data={'oauth_verifier': request.args['oauth_verifier']}
        )
        me = oauth_session.get('account/verify_credentials.json').json()
        social_id = 'twitter$' + str(me.get('id'))
        username = me.get('screen_name')
        return social_id, username, None   # Twitter does not provide email

In the callback() method the provider passes a verification token that the application can use to contact the provider's APIs. In the case of OAuth 2 this comes as a code argument, while for OAuth 1.0a it is oauth_verifier, both given in the query string. This code is used to obtain an oauth_session with the provider from the service object from rauth.

Note that in recent versions of the Facebook API, the session token is returned in JSON format. The default format that rauth expects for this token is to be provided in the query string of the request instead. For that reason, it is necessary to add a decoder argument that points to a function that decodes the JSON payload. In Python 2, it is sufficient to pass json.loads, but in Python 3 we need an additional step because the payload is returned as bytes, which the json parser does not understand. The conversion from bytes to string is done in the decode_json inner function.

The oauth_session object can be used to make API requests to the provider. Here it is used to request user information, which has to be done in a provider specific way. Facebook exposes a user id and an email, but does not give out usernames, so the username for the application is created from the left portion of the email address. Twitter provides the id and the username, but does not share emails, so the email is returned as None. The data obtained from the provider is finally returned as a three element tuple to the view function.

Note how in both cases the id value from the provider is prepended with "facebook$" or "twitter$" before it is returned, to make it unique across all providers. Since this is what the application will store as social_id in the database, it is necessary to do this to ensure that two providers that assign the same id to two different users do not collide in the application's database.

Conclusion

As I mentioned above, the example application allows any user to register and login with either a Facebook or a Twitter account. The application demonstrates how to register and login users without them having to enter any information, all they need to do is to login with the provider and authorize the sharing of information.

If you want to try this example, you need to follow some preparatory steps:

  • Clone or download the project's repository: https://github.com/miguelgrinberg/flask-oauth-example
  • Create a virtual environment and install the packages in the requirements.txt file (you can use Python 2.7 or 3.4).
  • Register "apps" with Facebook and Twitter, as described above.
  • Edit app.py with the id and secret codes of your Facebook and Twitter apps.

After you complete these instructions, you can run the application with python app.py, and then visit http://localhost:5000 in your browser.

I hope this article is useful in demystifying OAuth a little bit. If you have any questions feel free to write them below.

Miguel

Become a Patron!

Hello, and thank you for visiting my blog! If you enjoyed this article, please consider supporting my work on this blog on Patreon!

173 comments
  • #51 Eddie said

    Thanks for the great article and code example Miguel.

    Copying the code from the repo and I get a:
    flask-oauth-example/oauth.py", line 64, in callback
    Display the sourcecode for this frameOpen an interactive python shell in this frameme.get('email').split('@')[0], # Facebook does not provide
    AttributeError: 'NoneType' object has no attribute 'split'

    Am I missing something obvious here? Did Facebook change something thats causing this to break? Anyone else run into this problem?

  • #52 Eddie said

    @Nicholas @Miguel

    I was having the same problem as @Nicholas it was a change on facebook's end with the api. Here is the fix, just change:

    me = oauth_session.get('me').json()

    to be:

    me = oauth_session.get('me?fields=id,name,email').json()

  • #53 Miguel Grinberg said

    Thanks, Eddie!

  • #54 Alex Popov said

    @Miguel, than kyou for article, it helps me well. But I agree with Nelson - completely repeated your code and for facebook provider found that 'me' request in your code does not returns email for account.

  • #55 Alex Popov said

    Researched this issue and found that now Facebook provides code with email only when one more field provided, beside 'scope="email"' you should also pass 'info_fields: "email,name"' for request token parameters. See explanation here: https://github.com/mkdynamic/omniauth-facebook/issues/61#issuecomment-124914162

  • #56 Miguel Grinberg said

    @Alex: thanks for doing the research. It's odd that there is no mention of this in the FB API documentation (at least none that I can find).

  • #57 newbie said

    Hi Miguel,

    First off, thank you so much for your tutorials. I've come such a long way with Python in a very short time thanks to your microblog series. Now I have a question relating to Oauth.

    I've taken your samples and modified them to use oauth 1.0 to authenticate with another API exactly analogous to how your twitter example works, and it works great. However, I am having an issue relating to making additional calls later in the application flow (i.e not part of the initial authorization request) that utilize the same session without having to go through the authorization process again.

    In your sample you show how to make a call from the api from inside the callback function, which works great for getting data from the API at the time that you create the user session. But what I'm wanting to do is continue to make API calls later. I'm having trouble getting that session object to work with. You can't serialize the oauth_session itself, so you'd need to somehow re-constitute it from its tokens. It appears you need the oauth verifier as well as the request_token to call get_auth_session() but if I save the oauth verifier I get invalid session errors so I am guessing that oauth verifier only works once.

    You have the Oauth service living inside of a class and that class gets instantiated when authentication happens but then falls out of scope afterwards which seems to be why I can't just add additional methods to that class and reuse the session.

    How do you solve this problem so that the authorization the user does lasts throughout their session and you can make additional API calls using the tokens that are provided? Or so that the OAuthSignIn object that gets instantiated during login is still in scope for future requests?

    Ex.
    def random_method(): #this gets called sometime after authorization succeeds.
    if g.user is None or not g.user.is_autheticated():
    return redirect(url_for('login'))
    #now I want to call a method on the foreign api that I authorized with previously here
    results = apiAccessClass.callSomeAPIMethod()
    myform = RandomMethodForm(somedata=results)
    return render_template('somepage.html', form=myform)

    So far the only solutions I've seen are one amorphous python file serving all functions with no classes and no separation of concerns but that can't be the "right" answer. There has to be an elegant way to solve this problem I think, I'm just having trouble finding it.

    Thanks in advance!

  • #58 newbie said

    So I think I solved my own problem... probably typing it out for you helped me make the logic leap.
    I see now that the following is designed to make the OAuthSignIn derived class a singleton:
    @classmethod
    def get_provider(self, provider_name):
    if self.providers is None:
    self.providers = {}
    for provider_class in self.subclasses():
    provider = provider_class()
    self.providers[provider.provider_name] = provider
    return self.providers[provider_name]

    I made some changes to the class to create a class member for the oauth_session instead of leaving it in method scope. After doing that, the session was still in place for me when I retrieve the provider outside of the callback.

    Thanks again for the great tutorials.

  • #59 newbie said

    Sorry so many questions... but now that I see that get_providers is intended to be a singleton I have to ask, how is this multiple user safe? I think what I did before (saving the session in a class variable) would not be multiple user safe so somehow I think I am back to the problem of how do you recreate the actual oauth session from the token info on the session when you want to make a new call without reauthorizing?

  • #60 Miguel Grinberg said

    @newbie: first of all, the session object from the rauth library can be recreated if you have the access token. See https://rauth.readthedocs.org/en/latest/api/#oauth-2-0-sessions for info on that.

    The idea of the OAuth signin objects is that they are short lived, they are just little wrappers that are used during the authentication flow. I'm not sure how you can change things so that it saves the instances of this class, I think you must have been using just one client, this is not going to scale to multiple clients, I think.

    Saving the access token in the database and restoring the session object when you need to use it is the best solution, in my opinion.

  • #61 Sanyam Jain said

    Facebook returns NoneType object for email. I cannot figure out how to resolve it.

  • #62 Sanyam Jain said

    @Miguel I am a huge fan of yours and thank you for all the material you provide.
    I want to extract the user likes as soon as he logs in but I am to get them. Can you suggest me some idea to get them.Thank You in advance.

  • #63 Miguel Grinberg said

    @Sanyam: did you see comment #52 above?

  • #64 Miguel Grinberg said

    @Sanyam: regarding the likes, I'm not experienced with the Facebook API, so I don't know how to get them, or even if Facebook shares that information. The developer documentation for Facebook should give you the information that you need.

  • #65 Alexandru Stanciu said

    Hi Miguel, first authentication should ask the user to authorize the app (Twitter in my case), but subsequent ones should be treated like simple logins and not ask again for authorization as this was given already. I have the access tokens stored since the first authentication so can use those for subsequent ones..
    How do you suggest to implement that? Thank you for any hints.

  • #66 Miguel Grinberg said

    @Alexandru: Twitter/Facebook should remember the authorization on their side. In my experience, after the first authorization is given, any subsequent attempts do not require user intervention, until the user revokes access on their twitter/FB allowed apps page.

  • #67 winnielmz said

    Thank you for your great tutorial, Miguel! I download your repo from git and create a virtual environment for it, and replace the id and secret of Facebook. But when I click the link to authorize, I get the following error(I googled for solutions but didn't find one resolving my issue):
    {
    "error": {
    "message": "Invalid redirect_uri: \u5e94\u7528\u7a0b\u5e8f\u914d\u7f6e\u7981\u6b62\u4e86\u7ed9\u5b9a\u7f51\u5740",
    "type": "OAuthException",
    "code": 191,
    "fbtrace_id": "F3EtWQ2+aKd"
    }
    }
    Hope you can give me some advice.

  • #68 Miguel Grinberg said

    @winnielmz: the error seems to suggest the redirect URL that you are providing is bad. Have you checked that? Also, these characters in the URI are from the CJK portion of Unicode. Are you doing anything with Asian languages? I wonder if FB supports these characters.

  • #69 imdadhussain said

    i get these type of error so what should i do

    {
    "error": {
    "message": "Invalid redirect_uri: Given URL is not permitted by the Application configuration",
    "type": "OAuthException",
    "code": 191,
    "fbtrace_id": "Fls153tn2OT"
    }
    }

  • #70 Miguel Grinberg said

    @imdadhussain: sounds like you did not configure the correct callback URL in your app.

  • #71 Chinmaya said

    Any help on how to use login_view for each blueprint? I did a search on Google, your blogs, and every possible sources but didn't find any working examples.

    I have 4 blueprints as below:
    admin(init, route.py)
    customer(init, route.py)
    partners(init, route.py)
    auth(init, route.py, )

    "auth" is going to handle as a central point for different type(social+regular) of authentication.

    And I am using @login_required for all the possible routes under different profiles.
    But the problem is; "login_manager.login_view" only allows me to setup only one login view. I want to have separate login_views so that my respective authorization can be redirected to respective blueprints and so on and I can have a clear control on who(role) is logging in and it's respective route.

    I only found something @ http://flask-login.readthedocs.org/en/latest/#flask.ext.login.LoginManager.login_view that when
    unauthorized() is called it can first check "blueprint_login_views" and if not set then "login_views" is checked and then "HTTP 401 (Unauthorized) error" is raised if nothing is set. But there is no working example how to set "blueprint_login_views" through login_manager. Please help.

  • #72 Miguel Grinberg said

    @Chinmaya: never needed to do this myself, but I believe you should be able to do it using different LoginManager objects for each blueprint.

  • #73 Matt Campbell said

    Hi Miguel,

    Thanks so much for this tutorial, it works great as a stand alone flask app that doesn't use blueprints. However I'm running into trouble trying to implement this process in a factory application ( based on your book) that only implements a RESTful architecture.

    My problem, basically, after a successful authentication via OAuth the flask application doesn't know that a user was authenticated and will still ask for a username and password to use protected routes.

    using login_user(user, True) doesn't seem to log the user into the same application context.

    Any advice would be super helpful!

    Thanks,
    Matt

  • #74 Miguel Grinberg said

    @Matt: implementing OAuth for a strictly REST API is not possible, since the OAuth authentication flow requires you to host a web application. I assume you do have a couple of non-REST endpoints that serve a website, correct?

    Since all you care is to authenticate the user for REST API use, you will not be using Flask-Login. Instead, once you know who the user is, you can generate a token (exactly as shown in my book) and use that token to allow access to the API endpoints.

  • #75 John Sharples said

    Hi Miguel,

    Echoing those above, thanks for the great blog. I'm currently working through the microblog and came here since OpenID seems outdated.

    Apologies in advance if this is a silly question but I see several others have had similar issues, so thought I'd ask. I've got a bad callback url:

    {
    "error": {
    "message": "Missing redirect_uri parameter.",
    "type": "OAuthException",
    "code": 191,
    "fbtrace_id": "G2teFJgzvOW"
    }
    }

    From your comments above I gathered that I need to configure the callback url correctly. So, looking at the current callback url I get:

    http://localhost:5000/callback/facebook.

    Obviously this is of little use to facebook... I'm guessing that the blog implicitly assumes that you are developing on a device that is available on the external web. I'm working through these examples on a raspberry pi and guessing that I need to forward a port or something so that I can create an externally accessible callback .... any quick advice to get me going?

Leave a Comment