Authorization code flow with PKCE

The Authorization code flow with Proof Key for Code Exchange (PKCE) is an authorization method used to implement . This page provides an overview of the flow and explains how to implement it.

This flow is an extension of the original Authorization code flow with added security measures. To learn about the base Authorization code flow, go to Authorization code flow.

This is the recommended OAuth 2.0 flow for in client applications.

Proof Key for Code Exchange (PKCE)

Authorization code flow with PKCE
The Authorization code flow with PKCE. In addition to an authorization code, a code challenge and code verifier are also required

Proof Key for Code Exchange is an extension of the specification that adds an extra layer of security using a locally generated code_verifier and code_challenge. This ensures that the client that requests an is the same client that requested an . This prevents attackers from intercepting an authorization code returned from the authorization endpoint and using it to gain system access.

The general steps for the authorization code flow with PKCE are:

  1. Client generates a random code_verifier and code_challenge locally using SHA256 encryption.

  2. Client sends a request to the that includes the code_challenge as well as a client_id and redirect_uri from .

  3. The application user signs in with an . The verifies the user identity and returns an .

  4. Client uses the authorization_code and code_verifier, as well as the client_id and redirect_uri from , to submit a request to the . The token endpoint checks that the code_verifier aligns with the original code_challenge and issues an .

  5. Client uses the to authorize requests to .

ArcGIS APIs and SDKs

Authorization code with Proof Key for Code Exchange (PKCE) is the default authorization flow used by all ArcGIS and . To learn how to implement this flow using an ArcGIS API,

Manual implementation

The remainder of this page shows how to manually implement without using an ArcGIS or . Instead, authentication is implemented by directly making HTTP requests to the proper endpoints.

This sample is written in JavaScript, but can be implemented in any language. It adheres to the OAuth 2.0 specification for the Authorization code with Proof Key for Code Exchange (PKCE) flow.

Create OAuth credentials

requires a set of .

The steps to create OAuth credentials with an are:

  1. Sign in to your .

  2. Click Content > My content > New item and select .

  3. In the Credential types menu, select .

  4. Add a redirect URL and click Next.

  5. In the Privileges and Grant item access menus, click Next; these are not required for .

  6. Name the credentials and click Next to review. When you are ready to create the credentials, click Create.

Configure authentication variables

  1. Copy your client ID and chosen redirect URL from your and include them in your app.

    index.html
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
                const clientId = '<YOUR_CLIENT_ID>';
                const redirectUri = '<YOUR_REDIRECT_URL>';
    
                let session = null;
                let map = null;
                const signInButton = document.getElementById('sign-in');
                const signOutButton = document.getElementById('sign-out');
    

Generate a code verifier and challenge

  1. Use language-specific libraries to generate a code verifier. This string is required in the "Authorization code with PKCE" flow, and will be sent to the to request an access token.

    index.html
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
                const dec2hex = (dec) => {
                    return ('0' + dec.toString(16)).substr(-2)
                }
                const generateRandomString = () => {
                    var array = new Uint32Array(56/2);
                    window.crypto.getRandomValues(array);
                    return Array.from(array, dec2hex).join('');
                }
    
                const codeVerifier = generateRandomString();
    
    
  2. Generate a code challenge, a base64 SHA256 encrypted version of the code verifier. This string will be sent to the to obtain an authorization code.

    index.html
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
                const dec2hex = (dec) => {
                    return ('0' + dec.toString(16)).substr(-2)
                }
                const generateRandomString = () => {
                    var array = new Uint32Array(56/2);
                    window.crypto.getRandomValues(array);
                    return Array.from(array, dec2hex).join('');
                }
    
                const challengeFromVerifier = async (verifier) => {
                    // Generate SHA256 buffer
                    const encoder = new TextEncoder();
                    const data = encoder.encode(verifier);
                    const sha256 = await window.crypto.subtle.digest('SHA-256', data);
                    // Encode SHA256 as base 64
                    var str = "";
                    var bytes = new Uint8Array(sha256);
                    var len = bytes.byteLength;
                    for (var i = 0; i < len; i++) {
                        str += String.fromCharCode(bytes[i]);
                    }
                    const challenge = btoa(str)
                        .replace(/\+/g, "-")
                        .replace(/\//g, "_")
                        .replace(/=+$/, "");
                    return challenge;
                }
    
                const codeVerifier = generateRandomString();
    
                const codeChallenge = await challengeFromVerifier(codeVerifier);
    

Request an authorization code

  1. Format a GET request to the . Include your client_id, redirect_uri, and code_challenge.

    index.html
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
                const authorizationEndpoint = 'https://www.arcgis.com/sharing/rest/oauth2/authorize'+
                '?client_id=' + clientId +
                '&code_challenge=' + codeChallenge +
                '&code_challenge_method=S256' +
                '&redirect_uri=' + window.encodeURIComponent(redirectUri)+
                '&response_type=code'+
                '&expiration=20160';
    
    
  2. When the user clicks 'sign in', open the authorization endpoint in a new window.

    index.html
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
                const authorizationEndpoint = 'https://www.arcgis.com/sharing/rest/oauth2/authorize'+
                '?client_id=' + clientId +
                '&code_challenge=' + codeChallenge +
                '&code_challenge_method=S256' +
                '&redirect_uri=' + window.encodeURIComponent(redirectUri)+
                '&response_type=code'+
                '&expiration=20160';
    
                signInButton.addEventListener('click', () => window.open(authorizationEndpoint, 'oauth-window', 'height=400,width=600,menubar=no,location=yes,resizable=yes,scrollbars=yes,status=yes'));
    

Create a callback

  1. Create a callback function in index.html that will receive the authorization code.

    index.html
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
                const oauthCallback = (authorizationCode) => {
    
                }
                window.oauthCallback = oauthCallback; //required due to the module scope of this script
    
  2. Create a new HTML page at the location of your redirect URI. When a user successfully authenticates at the authorization endpoint, they will be redirected to this page.

    callback.html
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    <!DOCTYPE html>
      <head>
        <title>ArcGIS user authentication OAuth 2.0 callback (vanilla JS)</title>
      </head>
      <body>
        <script type="module">
    
        </script>
      </body>
    </html>
  3. Access the authorization code returned from the endpoint. It is found in the search parameter of the URL. Pass the code to the callback function created in index.html.

    callback.html
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    <!DOCTYPE html>
      <head>
        <title>ArcGIS user authentication OAuth 2.0 callback (vanilla JS)</title>
      </head>
      <body>
        <script type="module">
    
            const match = (window.location.search) ? window.location.search.match(/\?code=([^&]+)/) : false;
            // if we found an authorization code in the URL, pass the token up to a global function in index.html
            if(match[1]) {
                window.opener.oauthCallback(match[1]);
            }
            window.close();
    
        </script>
      </body>
    </html>

Request an access token

  1. Find the URL of the for your . For ArcGIS Online users, the token endpoint is https://www.arcgis.com/sharing/rest/oauth2/token.

    index.html
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
                const oauthCallback = (authorizationCode) => {
    
                    const tokenEndpoint = 'https://www.arcgis.com/sharing/rest/oauth2/token';
    
                }
                window.oauthCallback = oauthCallback; //required due to the module scope of this script
    
  2. Submit an HTTP request to the token endpoint to request an . Include your authorization_code, client_id, redirect_uri, and code_verifier to create a valid request, and set the grant_type as authorization_code.

    index.html
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
                const oauthCallback = (authorizationCode) => {
    
                    const tokenEndpoint = 'https://www.arcgis.com/sharing/rest/oauth2/token';
    
                    fetch(tokenEndpoint, {
                        method:'POST',
                        body: JSON.stringify({
                            client_id:clientId,
                            grant_type:'authorization_code',
                            code:authorizationCode,
                            redirect_uri:redirectUri,
                            code_verifier:codeVerifier
                        }),
                        headers: {
                            "Content-type": "application/json; charset=UTF-8"
                        }
                    })
    
                }
                window.oauthCallback = oauthCallback; //required due to the module scope of this script
    
  3. Access the response from the endpoint. If the request was valid, the response will contain an access_token, refresh_token, username, and other session information.

    index.html
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
                const oauthCallback = (authorizationCode) => {
    
                    const tokenEndpoint = 'https://www.arcgis.com/sharing/rest/oauth2/token';
    
                    fetch(tokenEndpoint, {
                        method:'POST',
                        body: JSON.stringify({
                            client_id:clientId,
                            grant_type:'authorization_code',
                            code:authorizationCode,
                            redirect_uri:redirectUri,
                            code_verifier:codeVerifier
                        }),
                        headers: {
                            "Content-type": "application/json; charset=UTF-8"
                        }
                    })
    
                    .then(response => response.json()).then(newSession => {
                        updateSession(newSession);
                        initApp(newSession);
                    })
    
                }
                window.oauthCallback = oauthCallback; //required due to the module scope of this script
    

Serialize session info

  1. Serialize the session information from the token endpoint and add it to local storage to make the session persistent across page refreshes.

    index.html
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
                const updateSession = (sessionInfo) => {
                    const userInfo = document.getElementById('user-info');
    
                    if (!sessionInfo) {
                        localStorage.removeItem("__ARCGIS_USER_SESSION__");
                        session = null;
    
                        destroyApp();
    
                        // signed out sidebar state
                        userInfo.classList.add('hide');
                        userInfo.innerHTML = ``;
                        signOutButton.classList.add('hide');
                        signInButton.classList.remove('hide');
                    }
    
                    else {
                        session = sessionInfo;
                        localStorage.setItem("__ARCGIS_USER_SESSION__", JSON.stringify(session));
    
                        // signed in sidebar state
                        userInfo.classList.remove('hide');
                        userInfo.innerHTML = `Welcome, ${sessionInfo.username}.`;
                        signOutButton.classList.remove('hide');
                        signInButton.classList.add('hide');
                    }
    
                }
    

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.

You can no longer sign into this site. Go to your ArcGIS portal or the ArcGIS Location Platform dashboard to perform management tasks.

Your ArcGIS portal

Create, manage, and access API keys and OAuth 2.0 developer credentials, hosted layers, and data services.

Your ArcGIS Location Platform dashboard

Manage billing, monitor service usage, and access additional resources.

Learn more about these changes in the What's new in Esri Developers June 2024 blog post.

Close