• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

oauthlib / oauthlib / 15026256536

14 May 2025 04:38PM UTC coverage: 93.054% (+0.06%) from 92.992%
15026256536

Pull #898

github

web-flow
Merge 4f9ce52cb into dab6a5ae1
Pull Request #898: 3.3.0 release

1004 of 1110 branches covered (90.45%)

Branch coverage included in aggregate %.

25 of 26 new or added lines in 9 files covered. (96.15%)

10 existing lines in 1 file now uncovered.

3082 of 3281 relevant lines covered (93.93%)

5.61 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.47
/oauthlib/oauth2/rfc6749/clients/base.py
1
# -*- coding: utf-8 -*-
2
"""
1✔
3
oauthlib.oauth2.rfc6749
4
~~~~~~~~~~~~~~~~~~~~~~~
5

6
This module is an implementation of various logic needed
7
for consuming OAuth 2.0 RFC6749.
8
"""
9
import base64
6✔
10
import hashlib
6✔
11
import time
6✔
12
import warnings
6✔
13

14
from oauthlib.common import UNICODE_ASCII_CHARACTER_SET, generate_token
6✔
15
from oauthlib.oauth2.rfc6749 import tokens
6✔
16
from oauthlib.oauth2.rfc6749.errors import (
6✔
17
    InsecureTransportError, TokenExpiredError,
18
)
19
from oauthlib.oauth2.rfc6749.parameters import (
6✔
20
    parse_token_response, prepare_token_request,
21
    prepare_token_revocation_request,
22
)
23
from oauthlib.oauth2.rfc6749.utils import is_secure_transport
6✔
24

25
AUTH_HEADER = 'auth_header'
6✔
26
URI_QUERY = 'query'
6✔
27
BODY = 'body'
6✔
28

29
FORM_ENC_HEADERS = {
6✔
30
    'Content-Type': 'application/x-www-form-urlencoded'
31
}
32

33

34
class Client:
6✔
35
    """Base OAuth2 client responsible for access token management.
36

37
    This class also acts as a generic interface providing methods common to all
38
    client types such as ``prepare_authorization_request`` and
39
    ``prepare_token_revocation_request``. The ``prepare_x_request`` methods are
40
    the recommended way of interacting with clients (as opposed to the abstract
41
    prepare uri/body/etc methods). They are recommended over the older set
42
    because they are easier to use (more consistent) and add a few additional
43
    security checks, such as HTTPS and state checking.
44

45
    Some of these methods require further implementation only provided by the
46
    specific purpose clients such as
47
    :py:class:`oauthlib.oauth2.MobileApplicationClient` and thus you should always
48
    seek to use the client class matching the OAuth workflow you need. For
49
    Python, this is usually :py:class:`oauthlib.oauth2.WebApplicationClient`.
50

51
    """
52
    refresh_token_key = 'refresh_token'
6✔
53

54
    def __init__(self, client_id,
6✔
55
                 default_token_placement=AUTH_HEADER,
56
                 token_type='Bearer',
57
                 access_token=None,
58
                 refresh_token=None,
59
                 mac_key=None,
60
                 mac_algorithm=None,
61
                 token=None,
62
                 scope=None,
63
                 state=None,
64
                 redirect_url=None,
65
                 state_generator=generate_token,
66
                 code_verifier=None,
67
                 code_challenge=None,
68
                 code_challenge_method=None,
69
                 **kwargs):
70
        """Initialize a client with commonly used attributes.
71

72
        :param client_id: Client identifier given by the OAuth provider upon
73
        registration.
74

75
        :param default_token_placement: Tokens can be supplied in the Authorization
76
        header (default), the URL query component (``query``) or the request
77
        body (``body``).
78

79
        :param token_type: OAuth 2 token type. Defaults to Bearer. Change this
80
        if you specify the ``access_token`` parameter and know it is of a
81
        different token type, such as a MAC, JWT or SAML token. Can
82
        also be supplied as ``token_type`` inside the ``token`` dict parameter.
83

84
        :param access_token: An access token (string) used to authenticate
85
        requests to protected resources. Can also be supplied inside the
86
        ``token`` dict parameter.
87

88
        :param refresh_token: A refresh token (string) used to refresh expired
89
        tokens. Can also be supplied inside the ``token`` dict parameter.
90

91
        :param mac_key: Encryption key used with MAC tokens.
92

93
        :param mac_algorithm:  Hashing algorithm for MAC tokens.
94

95
        :param token: A dict of token attributes such as ``access_token``,
96
        ``token_type`` and ``expires_at``.
97

98
        :param scope: A list of default scopes to request authorization for.
99

100
        :param state: A CSRF protection string used during authorization.
101

102
        :param redirect_url: The redirection endpoint on the client side to which
103
        the user returns after authorization.
104

105
        :param state_generator: A no argument state generation callable. Defaults
106
        to :py:meth:`oauthlib.common.generate_token`.
107

108
        :param code_verifier: PKCE parameter. A cryptographically random string that is used to correlate the
109
        authorization request to the token request.
110

111
        :param code_challenge: PKCE parameter. A challenge derived from the code verifier that is sent in the
112
        authorization request, to be verified against later.
113

114
        :param code_challenge_method: PKCE parameter. A method that was used to derive code challenge.
115
        Defaults to "plain" if not present in the request.
116
        """
117

118
        self.client_id = client_id
6✔
119
        self.default_token_placement = default_token_placement
6✔
120
        self.token_type = token_type
6✔
121
        self.access_token = access_token
6✔
122
        self.refresh_token = refresh_token
6✔
123
        self.mac_key = mac_key
6✔
124
        self.mac_algorithm = mac_algorithm
6✔
125
        self.token = token or {}
6✔
126
        self.scope = scope
6✔
127
        self.state_generator = state_generator
6✔
128
        self.state = state
6✔
129
        self.redirect_url = redirect_url
6✔
130
        self.code_verifier = code_verifier
6✔
131
        self.code_challenge = code_challenge
6✔
132
        self.code_challenge_method = code_challenge_method
6✔
133
        self.code = None
6✔
134
        self.expires_in = None
6✔
135
        self._expires_at = None
6✔
136
        self.populate_token_attributes(self.token)
6✔
137

138
    @property
6✔
139
    def token_types(self):
6✔
140
        """Supported token types and their respective methods
141

142
        Additional tokens can be supported by extending this dictionary.
143

144
        The Bearer token spec is stable and safe to use.
145

146
        The MAC token spec is not yet stable and support for MAC tokens
147
        is experimental and currently matching version 00 of the spec.
148
        """
149
        return {
6✔
150
            'Bearer': self._add_bearer_token,
151
            'MAC': self._add_mac_token
152
        }
153

154
    def prepare_request_uri(self, *args, **kwargs):
6✔
155
        """Abstract method used to create request URIs."""
156
        raise NotImplementedError("Must be implemented by inheriting classes.")
157

158
    def prepare_request_body(self, *args, **kwargs):
6✔
159
        """Abstract method used to create request bodies."""
160
        raise NotImplementedError("Must be implemented by inheriting classes.")
161

162
    def parse_request_uri_response(self, *args, **kwargs):
6✔
163
        """Abstract method used to parse redirection responses."""
164
        raise NotImplementedError("Must be implemented by inheriting classes.")
165

166
    def add_token(self, uri, http_method='GET', body=None, headers=None,
6✔
167
                  token_placement=None, **kwargs):
168
        """Add token to the request uri, body or authorization header.
169

170
        The access token type provides the client with the information
171
        required to successfully utilize the access token to make a protected
172
        resource request (along with type-specific attributes).  The client
173
        MUST NOT use an access token if it does not understand the token
174
        type.
175

176
        For example, the "bearer" token type defined in
177
        [`I-D.ietf-oauth-v2-bearer`_] is utilized by simply including the access
178
        token string in the request:
179

180
        .. code-block:: http
181

182
            GET /resource/1 HTTP/1.1
183
            Host: example.com
184
            Authorization: Bearer mF_9.B5f-4.1JqM
185

186
        while the "mac" token type defined in [`I-D.ietf-oauth-v2-http-mac`_] is
187
        utilized by issuing a MAC key together with the access token which is
188
        used to sign certain components of the HTTP requests:
189

190
        .. code-block:: http
191

192
            GET /resource/1 HTTP/1.1
193
            Host: example.com
194
            Authorization: MAC id="h480djs93hd8",
195
                                nonce="274312:dj83hs9s",
196
                                mac="kDZvddkndxvhGRXZhvuDjEWhGeE="
197

198
        .. _`I-D.ietf-oauth-v2-bearer`: https://tools.ietf.org/html/rfc6749#section-12.2
199
        .. _`I-D.ietf-oauth-v2-http-mac`: https://tools.ietf.org/html/rfc6749#section-12.2
200
        """
201
        if not is_secure_transport(uri):
6✔
202
            raise InsecureTransportError()
6✔
203

204
        token_placement = token_placement or self.default_token_placement
6✔
205

206
        case_insensitive_token_types = {
6✔
207
            k.lower(): v for k, v in self.token_types.items()}
208
        if self.token_type.lower() not in case_insensitive_token_types:
6✔
209
            raise ValueError("Unsupported token type: %s" % self.token_type)
6✔
210

211
        if not (self.access_token or self.token.get('access_token')):
6✔
212
            raise ValueError("Missing access token.")
6✔
213

214
        if self._expires_at and self._expires_at < time.time():
6✔
215
            raise TokenExpiredError()
6✔
216

217
        return case_insensitive_token_types[self.token_type.lower()](uri, http_method, body,
6✔
218
                                                                     headers, token_placement, **kwargs)
219

220
    def prepare_authorization_request(self, authorization_url, state=None,
6✔
221
                                      redirect_url=None, scope=None, **kwargs):
222
        """Prepare the authorization request.
223

224
        This is the first step in many OAuth flows in which the user is
225
        redirected to a certain authorization URL. This method adds
226
        required parameters to the authorization URL.
227

228
        :param authorization_url: Provider authorization endpoint URL.
229
        :param state: CSRF protection string. Will be automatically created if
230
            not provided. The generated state is available via the ``state``
231
            attribute. Clients should verify that the state is unchanged and
232
            present in the authorization response. This verification is done
233
            automatically if using the ``authorization_response`` parameter
234
            with ``prepare_token_request``.
235
        :param redirect_url: Redirect URL to which the user will be returned
236
            after authorization. Must be provided unless previously setup with
237
            the provider. If provided then it must also be provided in the
238
            token request.
239
        :param scope: List of scopes to request. Must be equal to
240
            or a subset of the scopes granted when obtaining the refresh
241
            token. If none is provided, the ones provided in the constructor are
242
            used.
243
        :param kwargs: Additional parameters to included in the request.
244
        :returns: The prepared request tuple with (url, headers, body).
245
        """
246
        if not is_secure_transport(authorization_url):
6✔
247
            raise InsecureTransportError()
6✔
248

249
        self.state = state or self.state_generator()
6✔
250
        self.redirect_url = redirect_url or self.redirect_url
6✔
251
        # do not assign scope to self automatically anymore
252
        scope = self.scope if scope is None else scope
6✔
253
        auth_url = self.prepare_request_uri(
6✔
254
            authorization_url, redirect_uri=self.redirect_url,
255
            scope=scope, state=self.state, **kwargs)
256
        return auth_url, FORM_ENC_HEADERS, ''
6✔
257

258
    def prepare_token_request(self, token_url, authorization_response=None,
6✔
259
                              redirect_url=None, state=None, body='', **kwargs):
260
        """Prepare a token creation request.
261

262
        Note that these requests usually require client authentication, either
263
        by including client_id or a set of provider specific authentication
264
        credentials.
265

266
        :param token_url: Provider token creation endpoint URL.
267
        :param authorization_response: The full redirection URL string, i.e.
268
            the location to which the user was redirected after successful
269
            authorization. Used to mine credentials needed to obtain a token
270
            in this step, such as authorization code.
271
        :param redirect_url: The redirect_url supplied with the authorization
272
            request (if there was one).
273
        :param state:
274
        :param body: Existing request body (URL encoded string) to embed parameters
275
                     into. This may contain extra parameters. Default ''.
276
        :param kwargs: Additional parameters to included in the request.
277
        :returns: The prepared request tuple with (url, headers, body).
278
        """
279
        if not is_secure_transport(token_url):
6✔
280
            raise InsecureTransportError()
6✔
281

282
        state = state or self.state
6✔
283
        if authorization_response:
6!
UNCOV
284
            self.parse_request_uri_response(
×
285
                authorization_response, state=state)
286
        self.redirect_url = redirect_url or self.redirect_url
6✔
287
        body = self.prepare_request_body(body=body,
6✔
288
                                         redirect_uri=self.redirect_url, **kwargs)
289

UNCOV
290
        return token_url, FORM_ENC_HEADERS, body
×
291

292
    def prepare_refresh_token_request(self, token_url, refresh_token=None,
6✔
293
                                      body='', scope=None, **kwargs):
294
        """Prepare an access token refresh request.
295

296
        Expired access tokens can be replaced by new access tokens without
297
        going through the OAuth dance if the client obtained a refresh token.
298
        This refresh token and authentication credentials can be used to
299
        obtain a new access token, and possibly a new refresh token.
300

301
        :param token_url: Provider token refresh endpoint URL.
302
        :param refresh_token: Refresh token string.
303
        :param body: Existing request body (URL encoded string) to embed parameters
304
            into. This may contain extra parameters. Default ''.
305
        :param scope: List of scopes to request. Must be equal to
306
            or a subset of the scopes granted when obtaining the refresh
307
            token. If none is provided, the ones provided in the constructor are
308
            used.
309
        :param kwargs: Additional parameters to included in the request.
310
        :returns: The prepared request tuple with (url, headers, body).
311
        """
312
        if not is_secure_transport(token_url):
6✔
313
            raise InsecureTransportError()
6✔
314

315
        # do not assign scope to self automatically anymore
316
        scope = self.scope if scope is None else scope
6✔
317
        body = self.prepare_refresh_body(body=body,
6✔
318
                                         refresh_token=refresh_token, scope=scope, **kwargs)
319
        return token_url, FORM_ENC_HEADERS, body
6✔
320

321
    def prepare_token_revocation_request(self, revocation_url, token,
6✔
322
                                         token_type_hint="access_token", body='', callback=None, **kwargs):
323
        """Prepare a token revocation request.
324

325
        :param revocation_url: Provider token revocation endpoint URL.
326
        :param token: The access or refresh token to be revoked (string).
327
        :param token_type_hint: ``"access_token"`` (default) or
328
            ``"refresh_token"``. This is optional and if you wish to not pass it you
329
            must provide ``token_type_hint=None``.
330
        :param body:
331
        :param callback: A jsonp callback such as ``package.callback`` to be invoked
332
            upon receiving the response. Not that it should not include a () suffix.
333
        :param kwargs: Additional parameters to included in the request.
334
        :returns: The prepared request tuple with (url, headers, body).
335

336
        Note that JSONP request may use GET requests as the parameters will
337
        be added to the request URL query as opposed to the request body.
338

339
        An example of a revocation request
340

341
        .. code-block:: http
342

343
            POST /revoke HTTP/1.1
344
            Host: server.example.com
345
            Content-Type: application/x-www-form-urlencoded
346
            Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
347

348
            token=45ghiukldjahdnhzdauz&token_type_hint=refresh_token
349

350
        An example of a jsonp revocation request
351

352
        .. code-block:: http
353

354
            GET /revoke?token=agabcdefddddafdd&callback=package.myCallback HTTP/1.1
355
            Host: server.example.com
356
            Content-Type: application/x-www-form-urlencoded
357
            Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
358

359
        and an error response
360

361
        .. code-block:: javascript
362

363
            package.myCallback({"error":"unsupported_token_type"});
364

365
        Note that these requests usually require client credentials, client_id in
366
        the case for public clients and provider specific authentication
367
        credentials for confidential clients.
368
        """
369
        if not is_secure_transport(revocation_url):
6✔
370
            raise InsecureTransportError()
6✔
371

372
        return prepare_token_revocation_request(revocation_url, token,
6✔
373
                                                token_type_hint=token_type_hint, body=body, callback=callback,
374
                                                **kwargs)
375

376
    def parse_request_body_response(self, body, scope=None, **kwargs):
6✔
377
        """Parse the JSON response body.
378

379
        If the access token request is valid and authorized, the
380
        authorization server issues an access token as described in
381
        `Section 5.1`_.  A refresh token SHOULD NOT be included.  If the request
382
        failed client authentication or is invalid, the authorization server
383
        returns an error response as described in `Section 5.2`_.
384

385
        :param body: The response body from the token request.
386
        :param scope: Scopes originally requested. If none is provided, the ones
387
            provided in the constructor are used.
388
        :return: Dictionary of token parameters.
389
        :raises: Warning if scope has changed. :py:class:`oauthlib.oauth2.errors.OAuth2Error`
390
            if response is invalid.
391

392
        These response are json encoded and could easily be parsed without
393
        the assistance of OAuthLib. However, there are a few subtle issues
394
        to be aware of regarding the response which are helpfully addressed
395
        through the raising of various errors.
396

397
        A successful response should always contain
398

399
        **access_token**
400
                The access token issued by the authorization server. Often
401
                a random string.
402

403
        **token_type**
404
            The type of the token issued as described in `Section 7.1`_.
405
            Commonly ``Bearer``.
406

407
        While it is not mandated it is recommended that the provider include
408

409
        **expires_in**
410
            The lifetime in seconds of the access token.  For
411
            example, the value "3600" denotes that the access token will
412
            expire in one hour from the time the response was generated.
413
            If omitted, the authorization server SHOULD provide the
414
            expiration time via other means or document the default value.
415

416
         **scope**
417
            Providers may supply this in all responses but are required to only
418
            if it has changed since the authorization request.
419

420
        .. _`Section 5.1`: https://tools.ietf.org/html/rfc6749#section-5.1
421
        .. _`Section 5.2`: https://tools.ietf.org/html/rfc6749#section-5.2
422
        .. _`Section 7.1`: https://tools.ietf.org/html/rfc6749#section-7.1
423
        """
424
        scope = self.scope if scope is None else scope
6✔
425
        self.token = parse_token_response(body, scope=scope)
6✔
426
        self.populate_token_attributes(self.token)
6✔
427
        return self.token
6✔
428

429
    def prepare_refresh_body(self, body='', refresh_token=None, scope=None, **kwargs):
6✔
430
        """Prepare an access token request, using a refresh token.
431

432
        If the authorization server issued a refresh token to the client, the
433
        client makes a refresh request to the token endpoint by adding the
434
        following parameters using the `application/x-www-form-urlencoded`
435
        format in the HTTP request entity-body:
436

437
        :param refresh_token: REQUIRED.  The refresh token issued to the client.
438
        :param scope:  OPTIONAL.  The scope of the access request as described by
439
            Section 3.3.  The requested scope MUST NOT include any scope
440
            not originally granted by the resource owner, and if omitted is
441
            treated as equal to the scope originally granted by the
442
            resource owner. Note that if none is provided, the ones provided
443
            in the constructor are used if any.
444
        """
445
        refresh_token = refresh_token or self.refresh_token
6✔
446
        scope = self.scope if scope is None else scope
6✔
447
        return prepare_token_request(self.refresh_token_key, body=body, scope=scope,
6✔
448
                                     refresh_token=refresh_token, **kwargs)
449

450
    def _add_bearer_token(self, uri, http_method='GET', body=None,
6✔
451
                          headers=None, token_placement=None):
452
        """Add a bearer token to the request uri, body or authorization header."""
453
        if token_placement == AUTH_HEADER:
6✔
454
            headers = tokens.prepare_bearer_headers(self.access_token, headers)
6✔
455

456
        elif token_placement == URI_QUERY:
6✔
457
            uri = tokens.prepare_bearer_uri(self.access_token, uri)
6✔
458

459
        elif token_placement == BODY:
6✔
460
            body = tokens.prepare_bearer_body(self.access_token, body)
6✔
461

462
        else:
463
            raise ValueError("Invalid token placement.")
6✔
464
        return uri, headers, body
6✔
465

466
    def create_code_verifier(self, length):
6✔
467
        """Create PKCE **code_verifier** used in computing **code_challenge**.
468
        See `RFC7636 Section 4.1`_
469

470
        :param length: REQUIRED. The length of the code_verifier.
471

472
        The client first creates a code verifier, "code_verifier", for each
473
        OAuth 2.0 [RFC6749] Authorization Request, in the following manner:
474

475
        .. code-block:: text
476

477
               code_verifier = high-entropy cryptographic random STRING using the
478
               unreserved characters [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~"
479
               from Section 2.3 of [RFC3986], with a minimum length of 43 characters
480
               and a maximum length of 128 characters.
481

482
        .. _`RFC7636 Section 4.1`: https://tools.ietf.org/html/rfc7636#section-4.1
483
        """
484
        code_verifier = None
6✔
485

486
        if not length >= 43:
6!
UNCOV
487
            raise ValueError("Length must be greater than or equal to 43")
×
488

489
        if not length <= 128:
6!
UNCOV
490
            raise ValueError("Length must be less than or equal to 128")
×
491

492
        code_verifier = generate_token(length, UNICODE_ASCII_CHARACTER_SET + "-._~")
6✔
493

494
        self.code_verifier = code_verifier
6✔
495

496
        return code_verifier
6✔
497

498
    def create_code_challenge(self, code_verifier, code_challenge_method=None):
6✔
499
        """Create PKCE **code_challenge** derived from the  **code_verifier**.
500
        See `RFC7636 Section 4.2`_
501

502
        :param code_verifier: REQUIRED. The **code_verifier** generated from `create_code_verifier()`.
503
        :param code_challenge_method: OPTIONAL. The method used to derive the **code_challenge**. Acceptable values include `S256`. DEFAULT is `plain`.
504

505
               The client then creates a code challenge derived from the code
506
               verifier by using one of the following transformations on the code
507
               verifier::
508

509
                   plain
510
                      code_challenge = code_verifier
511
                   S256
512
                      code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))
513

514
               If the client is capable of using `S256`, it MUST use `S256`, as
515
               `S256` is Mandatory To Implement (MTI) on the server.  Clients are
516
               permitted to use `plain` only if they cannot support `S256` for some
517
               technical reason and know via out-of-band configuration that the
518
               server supports `plain`.
519

520
               The plain transformation is for compatibility with existing
521
               deployments and for constrained environments that can't use the S256 transformation.
522

523
        .. _`RFC7636 Section 4.2`: https://tools.ietf.org/html/rfc7636#section-4.2
524
        """
525
        code_challenge = None
6✔
526

527
        if code_verifier is None:
6!
UNCOV
528
            raise ValueError("Invalid code_verifier")
×
529

530
        if code_challenge_method is None:
6✔
531
            code_challenge_method = "plain"
6✔
532
            self.code_challenge_method = code_challenge_method
6✔
533
            code_challenge = code_verifier
6✔
534
            self.code_challenge = code_challenge
6✔
535

536
        if code_challenge_method == "S256":
6✔
537
            h = hashlib.sha256()
6✔
538
            h.update(code_verifier.encode(encoding='ascii'))
6✔
539
            sha256_val = h.digest()
6✔
540
            code_challenge = bytes.decode(base64.urlsafe_b64encode(sha256_val))
6✔
541
            # replace '+' with '-', '/' with '_', and remove trailing '='
542
            code_challenge = code_challenge.replace("+", "-").replace("/", "_").replace("=", "")
6✔
543
            self.code_challenge = code_challenge
6✔
544

545
        return code_challenge
6✔
546

547
    def _add_mac_token(self, uri, http_method='GET', body=None,
6✔
548
                       headers=None, token_placement=AUTH_HEADER, ext=None, **kwargs):
549
        """Add a MAC token to the request authorization header.
550

551
        Warning: MAC token support is experimental as the spec is not yet stable.
552
        """
553
        if token_placement != AUTH_HEADER:
6!
UNCOV
554
            raise ValueError("Invalid token placement.")
×
555

556
        headers = tokens.prepare_mac_header(self.access_token, uri,
6✔
557
                                            self.mac_key, http_method, headers=headers, body=body, ext=ext,
558
                                            hash_algorithm=self.mac_algorithm, **kwargs)
559
        return uri, headers, body
6✔
560

561
    def _populate_attributes(self, response):
6✔
UNCOV
562
        warnings.warn("Please switch to the public method "
×
563
                      "populate_token_attributes.", DeprecationWarning)
UNCOV
564
        return self.populate_token_attributes(response)
×
565

566
    def populate_code_attributes(self, response):
6✔
567
        """Add attributes from an auth code response to self."""
568

569
        if 'code' in response:
6!
570
            self.code = response.get('code')
6✔
571

572
    def populate_token_attributes(self, response):
6✔
573
        """Add attributes from a token exchange response to self."""
574

575
        if 'access_token' in response:
6✔
576
            self.access_token = response.get('access_token')
6✔
577

578
        if 'refresh_token' in response:
6✔
579
            self.refresh_token = response.get('refresh_token')
6✔
580

581
        if 'token_type' in response:
6✔
582
            self.token_type = response.get('token_type')
6✔
583

584
        if 'expires_in' in response:
6✔
585
            self.expires_in = response.get('expires_in')
6✔
586
            self._expires_at = round(time.time()) + int(self.expires_in)
6✔
587

588
        if 'expires_at' in response:
6✔
589
            try:
6✔
590
                self._expires_at = round(float(response.get('expires_at')))
6✔
591
            except:
6✔
592
                self._expires_at = None
6✔
593

594
        if 'mac_key' in response:
6!
UNCOV
595
            self.mac_key = response.get('mac_key')
×
596

597
        if 'mac_algorithm' in response:
6!
UNCOV
598
            self.mac_algorithm = response.get('mac_algorithm')
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc