Oauth2 Flutter Authorization Parameters

216

So for anyone running into the same problem, so far I've found the answer is that you can't do it from the client side. This is an issue with the response returned from the server. If you don't have access to the server, you're SOL and will have to use a workaround like creating a proxy to send and accept all of your queries.

Specifically for my problem above, it was that the server did not include the following header

Access-Control-Expose-Headers:Location

Here are some other places to read about CORS that I found at least somewhat helpful:

Set cookies for cross origin requests

How to solve flutter web api cors error only with dart code?

https://github.com/flutterchina/dio/issues/1027

https://appvesto.medium.com/how-to-add-cors-to-the-dart-server-9d55a2835397

Share:
216
Grey
Author by

Grey

Updated on December 01, 2022

Comments

  • Grey
    Grey over 1 year

    I have a (possibly?) niche question about Oauth2 in Dart and Flutter (I'm using the standard Oauth2 package: https://pub.dev/packages/oauth2). Specifically, I need some values that are passed back as part of the Authorization flow, but they are not the standard values, so it's not returned as part of the client.

    Specifically, as part of my flow, I run:

          final returnValue = await authClient.authenticate(
            authorizationUrl: authorizationUrl,
            redirectUri: redirectUri!,
          );
    

    No problem there. Then I run:

          client = await grant
              .handleAuthorizationResponse(Uri.parse(returnValue).queryParameters);
    

    Again, works properly, returns a Client that I can use. However, following this function down, handleAuthorizationResponse calls _handleAuthorizationResponse. The function _handleAuthorizationResponse ends with:

        var response =
            await _httpClient!.post(tokenEndpoint, headers: headers, body: body);
        // print(response.headers);
        // print(response.body);
    
        var credentials = handleAccessTokenResponse(
            response, tokenEndpoint, startTime, _scopes, _delimiter,
            getParameters: _getParameters);
        return Client(credentials,
            identifier: identifier,
            secret: secret,
            basicAuth: _basicAuth,
            httpClient: _httpClient,
            onCredentialsRefreshed: _onCredentialsRefreshed);
    

    This is where my issue is. That response has some fields in the body that I need. They're not standard, so they're not passed back as part of the Client. Without rewriting my own versions of these functions, is there another way I can get access to these values?

    In answer to a question, this uses a SMART on FHIR launch (it's a launch framework that's basically just an oauth2 wrapper) - it's standard in healthcare. The return json from the accessToken would look something like this:

    {
      "need_patient_banner": true,
      "smart_style_url": "https://smart.argo.run/smart-style.json",
      "patient": "87a339d0-8cae-418e-89c7-8651e6aab3c6",
      "token_type": "Bearer",
      "scope": "launch/patient patient/Observation.rs patient/Patient.rs",
      "expires_in": 3600,
      "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuZWVkX3BhdGllbnRfYmFubmVyIjp0cnVlLCJzbWFydF9zdHlsZV91cmwiOiJodHRwczovL3NtYXJ0LmFyZ28ucnVuLy9zbWFydC1zdHlsZS5qc29uIiwicGF0aWVudCI6Ijg3YTMzOWQwLThjYWUtNDE4ZS04OWM3LTg2NTFlNmFhYjNjNiIsInRva2VuX3R5cGUiOiJiZWFyZXIiLCJzY29wZSI6ImxhdW5jaC9wYXRpZW50IHBhdGllbnQvT2JzZXJ2YXRpb24ucnMgcGF0aWVudC9QYXRpZW50LnJzIiwiY2xpZW50X2lkIjoiZGVtb19hcHBfd2hhdGV2ZXIiLCJleHBpcmVzX2luIjozNjAwLCJpYXQiOjE2MzM1MzIwMTQsImV4cCI6MTYzMzUzNTYxNH0.PzNw23IZGtBfgpBtbIczthV2hGwanG_eyvthVS8mrG4",
      "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb250ZXh0Ijp7Im5lZWRfcGF0aWVudF9iYW5uZXIiOnRydWUsInNtYXJ0X3N0eWxlX3VybCI6Imh0dHBzOi8vc21hcnQuYXJnby5ydW4vL3NtYXJ0LXN0eWxlLmpzb24iLCJwYXRpZW50IjoiODdhMzM5ZDAtOGNhZS00MThlLTg5YzctODY1MWU2YWFiM2M2In0sImNsaWVudF9pZCI6ImRlbW9fYXBwX3doYXRldmVyIiwic2NvcGUiOiJsYXVuY2gvcGF0aWVudCBwYXRpZW50L09ic2VydmF0aW9uLnJzIHBhdGllbnQvUGF0aWVudC5ycyBvZmZsaW5lX2FjY2VzcyIsImlhdCI6MTYzMzUzMzg1OSwiZXhwIjoxNjY1MDY5ODU5fQ.Q41QwZCEQlZ16M7YwvYuVbUP03mRFJoqRxL8SS8_ImM"
    }
    

    So it has the typical values of an accessToken (expires_in, token_type, etc), but it also has things like 'patient'. Those are the values that I need.

    In my example, the final Credentials (client.credentials looks like this):

    {
        "accessToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ1cm46b2lkOmZoaXIiLCJjbGllbnRfaWQiOiIzZmE1Y2FmOS04YTk4LTQ4MjgtOTJkZS01OWU2NmJjYjIwNjQiLCJlcGljLmVjaSI6InVybjplcGljOk9wZW4uRXBpYy1jdXJyZW50IiwiZXBpYy5tZXRhZGF0YSI6IjVYWnFRU0lrSk9PNi1XRkhpVXBiMlg2ak5MQzJ1aDFQdWtaSHVWSHkzcTdJMTBBT1BfYXR5V0tEb19LMlRFRF9ic21TNk9UTmZiVGtISnY1dnFvM1RtTVduRlk2RDBPSlE2WkRhd1NJWkk4WDh0Xy1XT1pYWEs1WjFrcTNBNm9mIiwiZXBpYy50b2tlbnR5cGUiOiJhY2Nlc3MiLCJleHAiOjE2NTQ2NDEzMDIsImlhdCI6MTY1NDYzNzcwMiwiaXNzIjoidXJuOm9pZDpmaGlyIiwianRpIjoiMDg1ZTdhNTYtY2I0OS00Zjg3LWFiYmEtZDg0M2ZmODI2YmQ2IiwibmJmIjoxNjU0NjM3NzAyLCJzdWIiOiJlYjRHaWE3RnlpanRQbVhrcnRqUnBQdzMifQ.rAKweImVE86oF3ciZDGhDysrYY9-XV6fBbyzqkQiJxHg-V-zImW414m3X5wKcP9B0J1MMdJCwg5DTpcbd0iU-N3SXRVXxBO2BqTcMAGLr-jlepnqBfu1Esg0nAI9jVasSWhz6tXFcLWOoCocg1hLcMfY875xnszwztJiJieDhumKZSStcsQM4KR9lUQZdJ3-U6IXV7wn3kaD4GQBSPZ0OkUe2d8zdCpjcbGCO-wWNdfe_sQDd7k7MbBJ1ryFRtd45GSzhKFa3Cch8kWTo3bGPlzzFuvhX_kbX1WtqTXaeB2G-o49lT4RJldnZi62L51VtS69_M15EsQtmMRHg6WMEA",
        "refreshToken": null,
        "idToken": "eyJhbGciOiJSUzI1NiIsImtpZCI6InRvVzlqTVVTTi81L0wzaXdhUUdkVG1ORHVodnAvSmNBWlZIL2NPSjZPckU9IiwidHlwIjoiSldUIn0.eyJhdWQiOiIzZmE1Y2FmOS04YTk4LTQ4MjgtOTJkZS01OWU2NmJjYjIwNjQiLCJleHAiOjE2NTQ2MzgwMDIsImZoaXJVc2VyIjoiaHR0cHM6Ly9maGlyLmVwaWMuY29tL2ludGVyY29ubmVjdC1maGlyLW9hdXRoL2FwaS9GSElSL1I0L1BhdGllbnQvZXJYdUZZVWZ1Y0JaYXJ5VmtzWUVjTWczIiwiaWF0IjoxNjU0NjM3NzAyLCJpc3MiOiJodHRwczovL2ZoaXIuZXBpYy5jb20vaW50ZXJjb25uZWN0LWZoaXItb2F1dGgvb2F1dGgyIiwic3ViIjoiZXJYdUZZVWZ1Y0JaYXJ5VmtzWUVjTWczIn0.hh33_q4f3tnioB7Iq6jY07-m5i_OsaqUt_kg_ZnPMGPKK8AnYVk3Tps2XTdUzUIHizFRWlGmAT_E0F283LBmVPTrbtD_X6EwqmUbTBrWj94RyvE-k3ofoEo-CwbSJZXu8MrQTb3DzpRKTGo7D1sI5E4UqnKQhPWFmhwCjMXpbdRy6bddb14fdWZzjS_Ffq4OsNRIalnePR8z1zNtSy14_RCiSh8o2elkj3p1AOmSXeD9-nZ91Z646lt4C5oP9gwN7OhmBovQRuDYaql1tz1aHOhilIsBZc1jMxEZJ65cekmFy6HZ4rME23xg-EQHu7XhKWOpOjovbMPwapSlC-eUcA",
        "tokenEndpoint": "https://fhir.epic.com/interconnect-fhir-oauth/oauth2/token",
        "scopes": [
            "patient/Patient.read",
            "patient/Questionnaire.read",
            "patient/QuestionnaireResponse.Read",
            "fhirUser",
            "launch/patient",
            "openid",
            "profile"
        ],
        "expiration": 1654641291566
    }
    

    I've tried decoding the accessToken and the idToken and neither of them have fields like "patient". Howevever, if after I get the response and print the body, it looks like this:

    {
        "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ1cm46b2lkOmZoaXIiLCJjbGllbnRfaWQiOiIzZmE1Y2FmOS04YTk4LTQ4MjgtOTJkZS01OWU2NmJjYjIwNjQiLCJlcGljLmVjaSI6InVybjplcGljOk9wZW4uRXBpYy1jdXJyZW50IiwiZXBpYy5tZXRhZGF0YSI6IkJJR1dLcDZPbU1ZcmJkT2YzMkFvX0djRmtiUG1RbEtZcHdEN1d4S0VLNEJXQTZvSUxXVUdRd05SUWdtMUdsd00xTzE0YUVZNi1GTmd6Si1jMGRiQ0puODRIUEU4SDEzNmp6UzNCZGFDUVBMSXFyYXpZenF3Uldyd1MwX3NWU1k5IiwiZXBpYy50b2tlbnR5cGUiOiJhY2Nlc3MiLCJleHAiOjE2NTQ2NDc2MDgsImlhdCI6MTY1NDY0NDAwOCwiaXNzIjoidXJuOm9pZDpmaGlyIiwianRpIjoiMTA1OTFmOTMtYjUxMi00MmNlLTk1YjgtYjY2NTEwODM0MzNkIiwibmJmIjoxNjU0NjQ0MDA4LCJzdWIiOiJlYjRHaWE3RnlpanRQbVhrcnRqUnBQdzMifQ.AJOd9g8YAJp91n0qY3Hg9F2sNpo26VMYKpNKR5y7CIV8zrADh2whv2WRm8gi-cIeS6XUR6UzXyzXVJ9Ips5FgFdIZ4yQI_HXxH9r8aeF6VS6jT-ZQygtzWnVYeyJvu-1b3YpbgdCd3KTrnWLwhU3vqUmil2L8gJzWG473ihXDz-7ezsJBBl9R-c5Ap_L6WF6Ox8lHH6mgwbZHeKr0U0aYne-QLM7mylsPC5BC_WlUOwMnEJ73DKjF2E0X6wMCP7jMieJxhpkTIDRwKQbuGwLjtneS-Efu69NHGsxSP_m3aN652rdh9-b5WyIsT-DqjPHxHTtbxGQI-WthHOhnLaDkQ",
        "token_type": "Bearer",
        "expires_in": 3600,
        "scope": "patient/Patient.read patient/Questionnaire.read patient/QuestionnaireResponse.Read fhirUser launch/patient openid profile",
        "__epic.dstu2.patient": "TnOZ.elPXC6zcBNFMcFA7A5KZbYxo2.4T-LylRk4GoW4B",
        "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6InRvVzlqTVVTTi81L0wzaXdhUUdkVG1ORHVodnAvSmNBWlZIL2NPSjZPckU9IiwidHlwIjoiSldUIn0.eyJhdWQiOiIzZmE1Y2FmOS04YTk4LTQ4MjgtOTJkZS01OWU2NmJjYjIwNjQiLCJleHAiOjE2NTQ2NDQzMDgsImZoaXJVc2VyIjoiaHR0cHM6Ly9maGlyLmVwaWMuY29tL2ludGVyY29ubmVjdC1maGlyLW9hdXRoL2FwaS9GSElSL1I0L1BhdGllbnQvZXJYdUZZVWZ1Y0JaYXJ5VmtzWUVjTWczIiwiaWF0IjoxNjU0NjQ0MDA4LCJpc3MiOiJodHRwczovL2ZoaXIuZXBpYy5jb20vaW50ZXJjb25uZWN0LWZoaXItb2F1dGgvb2F1dGgyIiwic3ViIjoiZXJYdUZZVWZ1Y0JaYXJ5VmtzWUVjTWczIn0.wxlvguGhAZdWJiSpX1-jzANXk0hFhLeIPFS5BlnIJLLZg8ibvpzLutQr2Z7Rg_d07_amI4gGbNigso9gvPbN5e1jjDGZkU2QYUbcLZbwkTcxXfVWOsyAADOZZrqx0J1yrGIeA4V4EfqQ4xBym_e8CeEjGP9L4ouRBKK6AHR5N5Mmdo_I4_RoPr-mCR2e2Q_of7tYFuhcl8mHaT6brbn-ZoEuAMgAQztF-7SBpDSvRB1C4HzV6mk-Hql0jNhZ0WefZe_ve0gB3exdWDjCLClpRRjt_MRaFTYGPqiZuyJF-dEFEqNar1Y5BRjQmUdJbDWj8ecfWaldigXNVAvNthbs4g",
        "patient": "erXuFYUfucBZaryVksYEcMg3"
    }
    

    So you can see why I need access to that information. Any idea, apart from extending the class and overriding the functions I could get access to it?

    • user18309290
      user18309290 almost 2 years
      Which authentication service you are using and what parameters you are expecting?
    • Grey
      Grey almost 2 years
      I updated my initial question to include that information.
  • Grey
    Grey almost 2 years
    I agree, in theory it should contain it. However, for the Epic (largest electronic health record in the US) sandbox, it does not. I'll add more details to my example to demonstrate this.