I have been working to integrate the Mailchimp OAuth implementation into an application that uses mailchimp mailing lists to drive call campaigns. My users login into my site using mailchimp, so you can see its going to be a very strong integration.
I have found a few problems with the implementation. This is about my 10th OAuth implementation so I am pretty familiar with all the little implementation differences. Here are a few crits, meant to be constructive.
Here are some details. I am using the Mailchimp python API on bitbucket and the Bananas_OAuth implementation.
Banana Py Oauth - https://github.com/claytantor/Banana-Py/blob/master/banana_py/__init__.py
Its seems as though the calls from https:This can be dealt with using the account-details method in the helper api.//login. don't return enough information to reliably create a user in the authenticating system and that the API does not have a method to get data on the OAuth user that the access token was generated for. This creates a problem when attempting to create a user for the first time. When user authenticates the following information should be in the response: email address, unique unchangeable id, and mail chimp username. Right now I have to use the "account_name" which can be changed. Can you see where this might be an issue? I also have to generate a fake email and force the user to verify the email when I could just use the one you verified already.mailchimp. com/oauth2/metadata - The OAuth implementation does not implement scope.
- The OAuth implementation does not use best practices for refresh tokens.
- The OAuth implementation is difficult to implement a flow for, I have implemented about 10 of these and I couldnt figure it out from your docs. If Bananas wast there I would have ended up spending an insane of of time.
- Making the API docs say that the calls use the API key is confusing I had to read deep to see that you could use an access token to make calls.
- Why doesn't it have a OAuth implementation in the API, c'mom just add Bananas_OAuth and do a little more architectural work to unify the concepts.
UPDATE: Cole from Mailchimp API got back to me in a day and gave me a solution for #1, I added this to the Gist as well. Looks like it works.
I have a Gist that shows some of the backflips I am having to do, its too bad too because with some very small changes the OAuth2 implementation and API would be pretty darn good. Check out what I am doing:
https://gist.github.com/claytantor/1d1169e0cc6d77d2c3ff
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# AUTH | |
# ****************************************************************************************** | |
# Step 1: Your application begins the authorization process by redirecting the user to the authorize_uri | |
# | |
# - this is a GET request | |
# - response_type=code, your client_id, and the *urlencoded* redirect_uri are included | |
# ****************************************************************************************** | |
# | |
# authorize_uri = https://login.mailchimp.com/oauth2/authorize?response_type=code&client_id=635959587059&redirect_uri=http%3A%2F%2F192.168.1.8%2Foauth%2Fcomplete.php | |
def auth_mc(request): | |
bannanas = Bananas_OAuth() | |
return redirect(bannanas.authorize_url()) | |
# ****************************************************************************************** | |
# Step 4: Your application must make an out-of-band request to the access_token_uri using the "code" returned | |
# | |
# - This is a POST request | |
# - as you can see, grant_type, client_id, client_secret, code, and redirect_uri are *all* POSTed | |
# ****************************************************************************************** | |
# | |
# access_token_uri: https://login.mailchimp.com/oauth2/token | |
# | |
# REQUEST: | |
# | |
# POST /oauth2/token HTTP/1.1 | |
# User-Agent: oauth2-draft-v10 | |
# Host: login.mailchimp.com | |
# Accept: application/json | |
# Content-Length: 198 | |
# Content-Type: application/x-www-form-urlencoded | |
# | |
# grant_type=authorization_code&client_id=635959587059&client_secret=0da3e7744949e1406b7b250051ee1a95&code=1edf2589e664fd317f6a7ff5f97b42f7&redirect_uri=http%3A%2F%2F192.168.1.8%2Foauth%2Fcomplete.php | |
def redirect_mc(request, tempate_name='redirect_mc.html'): | |
bannanas = Bananas_OAuth() | |
#{'access_token': 'secret', 'scope': None, 'expires_in': 0} | |
bannanas_auth = bannanas.authenticate(request.GET['code']) | |
# what the user info that comes from bananas | |
# This is a bad approach because what we really need here | |
# is some user info so we can either look up the user if they already exist | |
# or create the user if it doesnt. Using the accountname is not a good idea | |
# because it can change. | |
# { | |
# "login_url": "https://login.mailchimp.com", | |
# "access_token": "secret", | |
# "expires_in": 0, | |
# "dc": "us1", | |
# "accountname": "Your Account Name Can Change Inc.,", | |
# "api_endpoint": "https://us1.api.mailchimp.com", | |
# "role": "owner", | |
# "scope": null | |
# } | |
if bannanas_auth['access_token']: | |
#try to get the user info | |
#account-details | |
mc = mailchimp.Mailchimp(bannanas_auth['access_token']) | |
#account-details(string apikey, array exclude) | |
details = mc.helper.account_details() | |
try: | |
cp_user = CallpugUser.objects.get(username=details['user_id']) | |
cp_user.access_token = bannanas_auth['access_token'] | |
cp_user.save() | |
except ObjectDoesNotExist: | |
#use the mailchimp user id which will not change | |
cp_user = CallpugUser.objects.create_user( | |
details['user_id'], details['contact']['email'], | |
settings.CALLPUG_SECRET_KEY) | |
cp_user.integration_type='mailchimp' | |
cp_user.integration_id=details['user_id'] | |
cp_user.access_token=bannanas_auth['access_token'] | |
cp_user.save() | |
# authenticate the user, this shouldnt use the account name because it can change | |
# we use the mailchimp username_i to authenticate | |
print 'authenticating: {0}:{1}'.format(dedtails['user_id'],settings.CALLPUG_SECRET_KEY) | |
auth_user = authenticate(username=details['user_id'], | |
password=settings.CALLPUG_SECRET_KEY) | |
if auth_user is not None: | |
if auth_user.is_active: | |
login(request, auth_user) | |
# Redirect to a success page. | |
#needs the full app url for redirect | |
return redirect(reverse('user_home')) | |
else: | |
# # Return a 'disabled account' error message | |
# context['message']=request.POST['username']+' account has been suspended.' | |
return render_to_response('error.html',{'message':'auth user is not empty but us unactive'}, | |
context_instance=RequestContext(request)) | |
#flail | |
return render_to_response('error.html',{'message':'unknown problem with login'}, | |
context_instance=RequestContext(request)) |
I am also giving you a reference to an OAuth implementation that is solid and easy to implement: