3
votes

I'm developing an iOS app, utilizing the ROPC flow with AADB2C as the backend endpoints supporting this. https://login.microsoftonline.com/{TENANTNAME}.onmicrosoft.com/oauth2/v2.0/token?p={ROPC Policy Name}

I've successfully been able to request and retrieve an access token, refresh token and ID token upon a customer's first time successfully logging in using an email/password.

After this successful login, every subsequent login, we want to leverage biometrics (touch/face ID). My first thought was to store the refreshToken in the Keychain, check for the presence of a refreshToken before forcing a user to input his/her email/pw.

If a refreshToken exists, then I imagine I would use a call to the token endpoint, using a ?p=refresh_token as opposed to the ?p={INSERT ROPC Policy Name}and if I return a success, then I use Touch/Face ID to login.

The other thought I had was to just use the token ID to authenticate.

Thus my question is two-fold:

  1. What is the better practice - for iOS native application to use a refresh token or an ID token.

  2. I've tried using the refresh token, swapping out the {ROPC Policy Name} parameter with ?p=refresh_token, but every time I tried configuring the request, I get an error saying "The request body must contain the following parameter: 'grant_type'" I've added "refresh_token" as the key grant_type's value and that error still comes up. -- Why is that and how do I get around it if refresh token grant_type is better.

2

2 Answers

3
votes

You are correct.

You can save the refresh token to the key chain and protect the use of this refresh token with Face or Touch ID.

The "Redeem a refresh token" section of the "Configure the resource owner password credentials flow in Azure AD B2C" document describes how to redeem a refresh token that was issued for a resource owner policy:

POST /{tenant}.onmicrosoft.com/{policy}/oauth2/v2.0/token HTTP/1.1
Host: login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token
&response_type=id_token
&client_id={client_id}
&resource={client_id}
&refresh_token={refresh_token}
1
votes

Thanks @Chris Padgett. I was able to get it up and running with AppAuth using a slightly modified request. This was my code.

let authorizationEndpoint = URL(string: "https://login.microsoftonline.com/{TENANT_NAME}.onmicrosoft.com/oauth2/v2.0/authorize?p={ROPC_POLICY_NAME}")
let tokenEndpoint = URL(string: "https://login.microsoftonline.com/{TENANT_NAME}.onmicrosoft.com/oauth2/v2.0/token?p={ROPC_POLICY_NAME}")
let configuration = OIDServiceConfiguration(authorizationEndpoint: authorizationEndpoint!, tokenEndpoint: tokenEndpoint!)

//Configuring the token request
let tokenExchangeRequest = OIDTokenRequest(
    configuration: configuration,
    grantType: OIDGrantTypeRefreshToken,
    authorizationCode: nil,
    redirectURL: self.redirectUri!,
    clientID: self.clientId,
    clientSecret: nil,
    scope: "openid \(self.clientId) offline_access",
    refreshToken: {INSERT_REFRESH_TOKEN_HERE},
    codeVerifier: nil,
    additionalParameters: nil
)

//Performing token request
OIDAuthorizationService.perform(tokenExchangeRequest, callback: { tokenResponse, error in

    if tokenResponse == nil {
        print("Token request error: %@", error?.localizedDescription as Any)
    } else {
        guard let tokenResponse = tokenResponse else { return }
        ...handle tokenResponse how you need to...
    }
})