authenticationManager didReceive challenge not working with previously received token

633
8
Jump to solution
02-08-2024 06:52 AM
zdtorok
New Contributor II

We are using OAuth2 authorization to fetch an access token after logging in on the ESRI portal, and using that token later to authenticate basemaps.

Once the login page is dismissed we return to our mobile app with a redirect URL and store the token.

The token is requested via the Rest API described here: https://developers.arcgis.com/rest/users-groups-and-items/authorize.htm. We are using response_type='code' and no referer therefore.

Later, we did the following:

... {
    let challengeHandler = EsriAuthenticationChallengeHandler.shared
    challengeHandler.delegate = self
    AGSAuthenticationManager.shared().delegate = challengeHandler
    let oAuthConfiguration = AGSOAuthConfiguration(portalURL: portalURI,
                                                   clientID: clientID,
                                                   redirectURL: redirectURL)
    AGSAuthenticationManager.shared().oAuthConfigurations.add(oAuthConfiguration)
}

class EsriAuthenticationChallengeHandler: NSObject, AGSAuthenticationManagerDelegate {
    static let shared = EsriAuthenticationChallengeHandler()

    func authenticationManager(_ authenticationManager: AGSAuthenticationManager, didReceive challenge: AGSAuthenticationChallenge) {
        guard challenge.type == .oAuth else { return }
        challenge.continue(with: AGSCredential(token: esriToken, referer: nil))
    }
}

The token is correct, double checked that, but still nothing happens, the basemap itself is not rendered.

If I simply remove the `didReceive challenge:` method, it correctly displays the alert to require the username and password.

Am I missing something? Any help is appreciated.

0 Kudos
1 Solution

Accepted Solutions
NimeshJarecha
Esri Regular Contributor

Hi @zdtorok 

I would like know few things to narrow down the issue as well as understand your code and what you are trying to achieve.

1. What is the motivation of having your own OAuth implementation and generating tokens rather than using SDK's OAuth implementation?

2. If you are generating OAuth tokens on your own why are you providing the OAuth configuration on `AGSAuthenticationManager.shared().oAuthConfigurations`?

3. What is the portal url and basemaps url looks like?

4. How many challenges are issued and what are their request URLs?

5. Is basemap service federated with the portal you are generating OAuth token? If yes, you will have to generate a federated token using the portal (OAuth) token you have generated. See https://developers.arcgis.com/rest/users-groups-and-items/generate-token.htm

6. The SDK provides a way to debug the requests and responses. Please use following code and provide the generated log file.

let config = AGSRequestConfiguration.global()
config.debugLogFileURL = URL(fileURLWithPath: “<path to .md file”)
config.debugLogRequests = true
config.debugLogIncludeRequestHeaders = true
config.debugLogResponses = true
config.debugLogIncludeResponseHeaders = true
config.debugLogResponseTrimThreshold = 500
config.debugLogIgnoreTiledLayerRequests = true

 

Regards,

Nimesh

View solution in original post

0 Kudos
8 Replies
zdtorok
New Contributor II

Additional note: when checking the challenge.error, it shows "Token Required", error code 499, which is what I expected, but even after I try to continue the challenge (as described in the post above) with the token, it still has this error and no map is displayed.

0 Kudos
NimeshJarecha
Esri Regular Contributor

Hi @zdtorok 

I would like know few things to narrow down the issue as well as understand your code and what you are trying to achieve.

1. What is the motivation of having your own OAuth implementation and generating tokens rather than using SDK's OAuth implementation?

2. If you are generating OAuth tokens on your own why are you providing the OAuth configuration on `AGSAuthenticationManager.shared().oAuthConfigurations`?

3. What is the portal url and basemaps url looks like?

4. How many challenges are issued and what are their request URLs?

5. Is basemap service federated with the portal you are generating OAuth token? If yes, you will have to generate a federated token using the portal (OAuth) token you have generated. See https://developers.arcgis.com/rest/users-groups-and-items/generate-token.htm

6. The SDK provides a way to debug the requests and responses. Please use following code and provide the generated log file.

let config = AGSRequestConfiguration.global()
config.debugLogFileURL = URL(fileURLWithPath: “<path to .md file”)
config.debugLogRequests = true
config.debugLogIncludeRequestHeaders = true
config.debugLogResponses = true
config.debugLogIncludeResponseHeaders = true
config.debugLogResponseTrimThreshold = 500
config.debugLogIgnoreTiledLayerRequests = true

 

Regards,

Nimesh

0 Kudos
zdtorok
New Contributor II

Hi @NimeshJarecha ,

Thank you for your help.

1. We are using a cross-platform app with native extensions (one for Android, one for iOS) and the authentication is actually done outside of our native code (where we have esri) and the token is stored on the backend which we just query later on in our native code and pass it to the challenge handler.

2. I was not 100% sure if I have to still do such a configuration but thinking it through it seems not, as we only receive a token to pass on to the challenge handler, so I removed that part.

3. I was using https://myarcgis.portal.com but as described above I just removed that part so I guess this is not relevant anymore.

4. I receive one challenge. challenge.remoteResource?.url is https://tiles.arcgis.com/tiles/B129y13w1XikjUdj/arcgis/rest/services/MyMapService/MapServerand challenge.request.url is https://www.arcgis.com/sharing/rest/generateToken 

5. I'm not sure I know what you mean by federated token. But what is important that the same logic, with the same token works on Android but not on iOS. I personally haven't tested with my credentials but the logic works there.

6. Thank you for this debugging information, by using it I see the following in the log:

```  

 - response data **99 bytes**, `application/json;charset=utf-8`:  

```json
{
  "error" : {
    "message" : "You do not have permissions to use this resource.",
    "details" : null,
    "code" : 403
  }
}
```  

```json
Error: domain=com.esri.arcgis.runtime.services.error, code=403, description="You do not have permissions to use this resource.", failureReason:"(null)"
```  

-----  

 

So, I believe is it a permission issue? I also paste the full log below, because I see one other error which I don't understand: 405 Method not supported, but it seems to go forward after this so maybe not relevant?

**------>> sending request**  - `09/02/2024, 11:38:26 CET`  

 - [https://tiles.arcgis.com/tiles/B129y13w1XikjUdj/arcgis/rest/services/MyMapService/MapServer](https://tiles.arcgis.com/tiles/B129y13w1XikjUdj/arcgis/rest/services/MyMapService/MapServer?f=json)  
 - GET query:  
```
{
    f = json;
}
```  

 - request headers:  

```
{
    "User-Agent" = "ArcGISRuntime-iOS/100.15.4 (iPadOS 17.0.1; arm64; devmode) com.sap.mobile.apps.assetmanager/2405.0.0";
    "x-dynatrace" = "MT_3_3_68355509852373928_17-0_c84a8791-6b51-4344-ab97-9117e6cbfc30_0_73479_71";
}
```  

-----  

**------>> sending request**  - `09/02/2024, 11:38:26 CET`  

 - [https://tiles.arcgis.com/tiles/B129y13w1XikjUdj/arcgis/rest/info](https://tiles.arcgis.com/tiles/B129y13w1XikjUdj/arcgis/rest/info?f=json)  
 - GET query:  
```
{
    f = json;
}
```  

-----  

**<<------ received response**  - `09/02/2024, 11:38:26 CET`  

 - [https://tiles.arcgis.com/tiles/B129y13w1XikjUdj/arcgis/rest/info](https://tiles.arcgis.com/tiles/B129y13w1XikjUdj/arcgis/rest/info?f=json)  
 - GET query:  
```
{
    f = json;
}
```  

 - response status code **200**  

 - response data **231 bytes**, `application/json;charset=utf-8`:  

```json
{"owningSystemUrl":"https://www.arcgis.com","fullVersion":"10.8.1","owningTenant":"B129y13w1XikjUdj","currentVersion":10.81,"authInfo":{"tokenServicesUrl":"https://www.arcgis.com/sharing/generateToken","isTokenBasedSecurity":true}}
```  

-----  

**------>> sending request**  - `09/02/2024, 11:38:26 CET`  

 - [https://www.arcgis.com/sharing/rest/generateToken](https://www.arcgis.com/sharing/rest/generateToken?f=json)  
 - GET query:  
```
{
    f = json;
}
```  

-----  

**<<------ received response**  - `09/02/2024, 11:38:26 CET`  

 - [https://www.arcgis.com/sharing/rest/generateToken](https://www.arcgis.com/sharing/rest/generateToken?f=json)  
 - GET query:  
```
{
    f = json;
}
```  

 - response status code **200**  

 - response data **94 bytes**, `text/plain;charset=utf-8`:  

```json
{
  "error" : {
    "code" : 405,
    "messageCode" : "GWM_0005",
    "message" : "Method not supported.",
    "details" : [

    ]
  }
}
```  

```
Error: domain=com.esri.arcgis.runtime.services.error, code=405, description="Method not supported.", failureReason:"(null)"
```  

-----  

**------>> sending request**  - `09/02/2024, 11:38:26 CET`  

 - [https://www.arcgis.com/sharing/rest/info](https://www.arcgis.com/sharing/rest/info?f=json)  
 - GET query:  
```
{
    f = json;
}
```  

-----  

**<<------ received response**  - `09/02/2024, 11:38:27 CET`  

 - [https://www.arcgis.com/sharing/rest/info](https://www.arcgis.com/sharing/rest/info?f=json)  
 - GET query:  
```
{
    f = json;
}
```  

 - response status code **200**  

 - response data **156 bytes**, `text/plain;charset=utf-8`:  

```
{"owningSystemUrl":"https://www.arcgis.com","authInfo":{"tokenServicesUrl":"https://www.arcgis.com/sharing/rest/generateToken","isTokenBasedSecurity":true}}
```  

-----  

**------>> sending request**  - `09/02/2024, 11:38:27 CET`  

 - [https://www.arcgis.com/sharing/rest/](https://www.arcgis.com/sharing/rest/?f=json)  
 - GET query:  
```
{
    f = json;
}
```  

-----  

**<<------ received response**  - `09/02/2024, 11:38:27 CET`  

 - [https://www.arcgis.com/sharing/rest/](https://www.arcgis.com/sharing/rest/?f=json)  
 - GET query:  
```
{
    f = json;
}
```  

 - response status code **200**  

 - response data **27 bytes**, `text/plain;charset=utf-8`:  

```
{"currentVersion":"2023.3"}
```  

-----  

**------>> sending request**  - `09/02/2024, 11:38:27 CET`  

 - [https://www.arcgis.com/sharing/rest/portals/self](https://www.arcgis.com/sharing/rest/portals/self?f=json&culture=en_HU)  
 - GET query:  
```
{
    culture = "en_HU";
    f = json;
}
```  

-----  

**<<------ received response**  - `09/02/2024, 11:38:27 CET`  

 - [https://www.arcgis.com/sharing/rest/portals/self](https://www.arcgis.com/sharing/rest/portals/self?f=json&culture=en_HU)  
 - GET query:  
```
{
    culture = "en_HU";
    f = json;
}
```  

 - response status code **200**  

 - response data **10240 bytes**, `text/plain;charset=utf-8`:  

```
{
    2DStylesGroupQuery = "title:\"Esri 2D Styles\" AND owner:esri_en";
    3DBasemapGalleryGroupQuery = "title:\"ArcGIS Online 3D Basemaps\" AND owner:esri";
    analysisLayersGroupQuery = "title:\"Living Atlas Analysis Layers\" AND owner:esri";
    basemapGalleryGroupQuery = "title:\"ArcGIS Online Basemaps\" AND owner:esri_en";
    cdnUrl = "https://cdn.arcgis.com";
    colorSetsGroupQuery = "title:\"Esri Colors\" AND owner:esri_en";
    contentCategorySetsGroupQuery = "title:\"ArcGIS Online ...
```  

-----  

**------>> sending request**  - `09/02/2024, 11:38:27 CET`  

 - [https://www.arcgis.com/sharing/rest/generateToken](https://www.arcgis.com/sharing/rest/generateToken?token=*****&serverUrl=https%3A%2F%2Ftiles.arcgis.com%2Ftiles&client=referer&referer=arcgisios&f=json)  
 - POST body:  
```
{
    client = referer;
    f = json;
    referer = arcgisios;
    serverUrl = "https://tiles.arcgis.com/tiles";
}
```  

 - request headers:  

```
{
    "User-Agent" = "ArcGISRuntime-iOS/100.15.4 (iPadOS 17.0.1; arm64) com.sap.mobile.apps.assetmanager/2405.0.0";
    "x-dynatrace" = "MT_3_3_68355509852373928_17-0_c84a8791-6b51-4344-ab97-9117e6cbfc30_0_259_80";
}
```  

-----  

**<<------ received response**  - `09/02/2024, 11:38:27 CET`  

 - [https://www.arcgis.com/sharing/rest/generateToken](https://www.arcgis.com/sharing/rest/generateToken?token=*****&serverUrl=https%3A%2F%2Ftiles.arcgis.com%2Ftiles&client=referer&referer=arcgisios&f=json)  
 - POST body:  
```
{
    client = referer;
    f = json;
    referer = arcgisios;
    serverUrl = "https://tiles.arcgis.com/tiles";
}
```  

 - response status code **200**  

 - response headers:  

```
{
    "Cache-Control" = "no-cache";
    "Content-Encoding" = gzip;
    "Content-Type" = "text/plain;charset=utf-8";
    Date = "Fri, 09 Feb 2024 10:38:27 GMT";
    Expires = "-1";
    Pragma = "no-cache";
    "Strict-Transport-Security" = "max-age=31536000";
    Vary = "Origin, Accept-Encoding";
    "x-content-type-options" = nosniff;
}
```  

 - response data **391 bytes**, `text/plain;charset=utf-8`:  

```
{token:"*****","expires":1707476771801,"ssl":true}
```  

-----  

**------>> sending request**  - `09/02/2024, 11:38:27 CET`  

 - [https://tiles.arcgis.com/tiles/B129y13w1XikjUdj/arcgis/rest/services/MyMapService/MapServer](https://tiles.arcgis.com/tiles/B129y13w1XikjUdj/arcgis/rest/services/MyMapService/MapServer?f=json)  
 - GET query:  
```
{
    f = json;
}
```  

 - request headers:  

```
{
    Referer = arcgisios;
    "User-Agent" = "ArcGISRuntime-iOS/100.15.4 (iPadOS 17.0.1; arm64) com.sap.mobile.apps.assetmanager/2405.0.0";
    "X-Esri-Authorization" = "*****";
    "x-dynatrace" = "MT_3_3_68355509852373928_17-0_c84a8791-6b51-4344-ab97-9117e6cbfc30_0_259_83";
}
```  

-----  

**<<------ received response**  - `09/02/2024, 11:38:27 CET`  

 - [https://tiles.arcgis.com/tiles/B129y13w1XikjUdj/arcgis/rest/services/MyMapService/MapServer](https://tiles.arcgis.com/tiles/B129y13w1XikjUdj/arcgis/rest/services/MyMapService/MapServer?f=json)  
 - GET query:  
```
{
    f = json;
}
```  

 - response status code **200**  

 - response headers:  

```
{
    "Cache-Control" = "private, no-cache, no-store, no-transform, must-revalidate, max-age=0, s-maxage=0";
    "Content-Encoding" = gzip;
    "Content-Type" = "application/json;charset=utf-8";
    Date = "Fri, 09 Feb 2024 10:38:27 GMT";
    "Strict-Transport-Security" = "max-age=63072000";
    Vary = "Origin,Accept-Encoding,Authorization,X-Esri-Authorization";
    Via = "1.1 4a0b7683a1d33d6d186965e831f2de96.cloudfront.net (CloudFront)";
    "x-amz-cf-id" = "662YIV2XTQ9PsR08-bXLz-IxcWp_SdFohX-DY8SUu9q7Rbe232xzZA==";
    "x-amz-cf-pop" = "FRA56-P7";
    "x-arcgis-instance-id" = "i-0c903a2da99a46d4f";
    "x-cache" = "Miss from cloudfront";
    "x-content-type-options" = nosniff;
    "x-powered-by" = "ArcGIS-Online-Hosted-Services";
    "x-xss-protection" = "1; mode=block";
}
```  

 - response data **99 bytes**, `application/json;charset=utf-8`:  

```json
{
  "error" : {
    "message" : "You do not have permissions to use this resource.",
    "details" : null,
    "code" : 403
  }
}
```  

```json
Error: domain=com.esri.arcgis.runtime.services.error, code=403, description="You do not have permissions to use this resource.", failureReason:"(null)"
```  

-----  

 

0 Kudos
zdtorok
New Contributor II

I noticed we might receive another challenge to, not clear yet why, but we have.

The auth request url is the same as above and the resource is 

https://services1.arcgis.com/B129y13w1XikjUdj/arcgis/rest/services/authenticatedlayer/FeatureServer/...

**<<------ received response**  - `09/02/2024, 12:48:37 CET`  

 - [https://services1.arcgis.com/B129y13w1XikjUdj/arcgis/rest/services/usace_regulatory_boundary/FeatureServer/0](https://services1.arcgis.com/B129y13w1XikjUdj/arcgis/rest/services/usace_regulatory_boundary/FeatureServer/0?f=json&returnAdvancedSymbols=true)  
 - GET query:  
```
{
    f = json;
    returnAdvancedSymbols = true;
}
```  

 - response status code **200**  

 - response headers:  

```
{
    "Access-Control-Allow-Origin" = "*";
    "Cache-Control" = "no-cache";
    "Content-Encoding" = br;
    "Content-Length" = 106;
    "Content-Type" = "application/json; charset=utf-8";
    Date = "Fri, 09 Feb 2024 11:48:37 GMT";
    Expires = "-1";
    Pragma = "no-cache";
    "Strict-Transport-Security" = "max-age=63072000";
    Vary = "X-Esri-Authorization";
    Via = "1.1 63f49fb8153ef60b0374321d41a091c6.cloudfront.net (CloudFront)";
    "request-context" = "appId=cid-v1:02d88829-cd35-4b36-83db-f89bb9f85514";
    "x-amz-cf-id" = "WBXpDfylH8x6JXjO2GQ6g9jGINXkzAQlSauzRmgGgxKbHLAutqi2xg==";
    "x-amz-cf-pop" = "BUD50-P2";
    "x-arcgis-correlation-id" = "00-6257df42e3fcde797f549244e8589879-15c3d9269b4526f0-00";
    "x-arcgis-instance" = i7tdixsfq00000H;
    "x-arcgis-trace-id" = 6257df42e3fcde797f549244e8589879;
    "x-arcgis-upstream" = us1h01c00;
    "x-cache" = "Miss from cloudfront";
}
```  

 - response data **178 bytes**, `application/json; charset=utf-8`:  

```json
{
  "error" : {
    "code" : 403,
    "message" : "Subscription is disabled, the item is not accessible",
    "messageCode" : "SB_0005",
    "details" : [
      "Subscription is disabled, the item is not accessible"
    ]
  }
}
```  

```json
Error: domain=com.esri.arcgis.runtime.services.error, code=403, description="Subscription is disabled, the item is not accessible", failureReason:"Subscription is disabled, the item is not accessible"
```  

----- 

This is the log which gives a slightly different 403 error:

 

0 Kudos
zdtorok
New Contributor II

Another strange issue, can it happen that for the same user for the same resource with the same token handling, on iOS we receive an 403 error but on Android it works? One of my colleague seems to have it working on Android but on iOS it's not working with the same credentials.

0 Kudos
NimeshJarecha
Esri Regular Contributor

Hi @zdtorok ,

I receive one challenge. challenge.remoteResource?.url is https://tiles.arcgis.com/tiles/B129y13w1XikjUdj/arcgis/rest/services/MyMapService/MapServerand challenge.request.url is https://www.arcgis.com/sharing/rest/generateToken 

The challenge is issued for the portal (https://www.arcgis.com) and the layer (https://tiles.arcgis.com/tiles/B129y13w1XikjUdj/arcgis/rest/services/MyMapService/MapServer) is federated with the portal. The credential with token you are providing is the portal token. The following request creates a federated token with the token you have provided.

 - [https://www.arcgis.com/sharing/rest/generateToken](https://www.arcgis.com/sharing/rest/generateToken?token=*****&serverUrl=https%3A%2F%2Ftiles.arcgis.com%2Ftiles&client=referer&referer=arcgisios&f=json)  
 - POST body:  
```
{
    client = referer;
    f = json;
    referer = arcgisios;
    serverUrl = "https://tiles.arcgis.com/tiles";
}
```  

 

The layer is not accessible with the federated token and fails with an error.

{
  "error" : {
    "message" : "You do not have permissions to use this resource.",
    "details" : null,
    "code" : 403
  }
}

 

The error for another layer seems to indicate the same that it is not accessible with the given token.

{
  "error" : {
    "code" : 403,
    "message" : "Subscription is disabled, the item is not accessible",
    "messageCode" : "SB_0005",
    "details" : [
      "Subscription is disabled, the item is not accessible"
    ]
  }
}

 

Another strange issue, can it happen that for the same user for the same resource with the same token handling, on iOS we receive an 403 error but on Android it works

I doubt, but would like to see the side-by-side logs with actual token values.

 

Regards,

Nimesh

0 Kudos
zdtorok
New Contributor II

Hi @NimeshJarecha ,

Thank you for your help. After checking it again it came out that I received wrong information and we have received 403 on both platform so there was no difference.

After we changed our test feature layer to one to which we actually had correct access and permissions set up, the whole flow seems working.

So, thanks for your help especially with the debug code you provided, that was very useful, without that we'd still be debugging without any hint on the 403 error.

Regards,
Zoltán

0 Kudos
NimeshJarecha
Esri Regular Contributor

Hi @zdtorok,

I'm glad to hear that the issue has been resolved and that the flow is now working correctly. Thank you for letting me know and for your kind words.

Regards,

Nimesh

0 Kudos