0

I want to read outlook emails and save the attachments, and I'm using python-O365 module for that. The problem is this module requires account authentication in order to access outlook.

The workflow is in this way:

  1. User accesses the function/api, which then uses predefined/hardcoded credentials to connect to the outlook account.
client = "XXXXXXXXXX-XXXX-XXXX-XXXXXXXXXXXXXXX"
secret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
credentials = (client, secret)
account = Account(credentials)
  1. At this point the function provides a url in the console for the user to go visit and provide consent and asks the user to paste the authenticated url back in the console. Image below for reference.

Console Image

The problem here is that I want this authentication to be done on UI, not in the console. Im pushing this API to a server, where it will be not possible for the user to access the console to get this url and paste back the authenticated url.

Is there a way to either skip this authentication on whole? Or atleast a way to redirect the user directly to this mentioned url in console and provide the authenticated url to console directly from UI?

Abhinav
  • 125
  • 6
  • I'd suggest reading about how an oAuth flow works as it requires a user to be redirected to the generated URL to enter their credentials before being redirected back with a temp token to be exchanged for an access and refresh token. I'd suggest looking into something like Flask and an oAuth2 library which does some of this for you. I often read Miguel's articles https://blog.miguelgrinberg.com/post/oauth-authentication-with-flask which might help. – Johnny John Boy Nov 23 '22 at 09:03
  • I looked into authentication with Flask, but it seemed like what I want to do is not possible with that type of authentication. I'm initializing an Account obj and using it over here, but in Flask we have to use GET requests to API, which I dont think provides me the freedom to do what I want. – Abhinav Nov 23 '22 at 11:15
  • Do you know if we can automate this authentication using oAuth? – Abhinav Nov 23 '22 at 11:15
  • 1
    A user is always going to have to redirect and authenticate against O365. When you're redirected back to the URL you've provided to Microsoft, that URL should be capturing the query string that's returned. Effectively step 1 is use the library to generate the URL to give consent, step 2 is use Flask to redirect to it, step 3 is upon return capture the url parameters required in the command line process. I've done both ways but never give a user a command line UI for the same reason as you regarding a console. – Johnny John Boy Nov 23 '22 at 12:24

1 Answers1

1

I got my answer myself. Basically I imported the functions that are being used in O365 library into my code, and reworked them a bit to get what I wanted done.

Here it goes,

So by default on a GET request, this django API shows the link that user needs to visit, sign-in and provide consent.(client and secret are hardcoded).

consent_url, _ = con.get_authorization_url(**kwargs) This line of code is being used in oauth_authentication_flow function in O365 module to print out the consent_url in console. I used it to just return the consent_url to UI.

Once user sign-in and consent is provided and they copy the token-url to paste it back to console, result = con.request_token(token_url, **kwargs) this line of code is used in the same oauth_authentication_flow function in O365 module to check if access token and refresh token are successfully generated and stored.

So using a POST request, now a user can submit the token_url back to my django API to get access to O365 api without relying on console.

@api_view(['GET','POST'])
def setupMail(request,**kwargs):
    client = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    secret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    credentials = (client, secret)
    scopes=['basic', 'message_all']
    global account
    account = Account(credentials)
    protocol = MSGraphProtocol()
    con = O365.Connection(credentials, scopes=protocol.get_scopes_for(scopes),**kwargs)
    if request.method == "GET":
        consent_url, _ = con.get_authorization_url(**kwargs)
        return Response('Visit the following url to give consent: ' + consent_url)
    if request.method == "POST":
        token_url = request.data.get('token')
        if token_url:
            result = con.request_token(token_url, **kwargs)  # no need to pass state as the session is the same
            if result:
                return Response('Authentication Flow Completed. Oauth Access Token Stored. '
                        'You can now use the API.')
            else:
                return Response('Something go wrong. Please try again. ' + str(bool(result)))
        else:
            return Response('Authentication Flow aborted.')
    else:
        return Response('Bad Request',status=status.HTTP_400_BAD_REQUEST)

Please let me know if there are any security concerns that I need to be worried about.

Abhinav
  • 125
  • 6