Reputation: 105
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:
What is the better practice - for iOS native application to use a refresh token or an ID token.
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.
Upvotes: 4
Views: 1033
Reputation: 105
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...
}
})
Upvotes: 1
Reputation: 14724
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}
Upvotes: 3