def test_username_password_credential():
    expected_token = "access-token"
    client_id = "client-id"
    transport = validating_transport(
        requests=[Request()] *
        3,  # not validating requests because they're formed by MSAL
        responses=[
            # tenant discovery
            mock_response(
                json_payload={
                    "authorization_endpoint": "https://a/b",
                    "token_endpoint": "https://a/b"
                }),
            # user realm discovery, interests MSAL only when the response body contains account_type == "Federated"
            mock_response(json_payload={}),
            # token request
            mock_response(
                json_payload=build_aad_response(access_token=expected_token,
                                                id_token=build_id_token(
                                                    aud=client_id))),
        ],
    )

    credential = UsernamePasswordCredential(
        client_id=client_id,
        username="******",
        password="******",
        transport=transport,
        instance_discovery=
        False,  # kwargs are passed to MSAL; this one prevents an AAD verification request
    )

    token = credential.get_token("scope")
    assert token.token == expected_token
Exemplo n.º 2
0
def get_account_event(username,
                      uid,
                      utid,
                      authority=None,
                      client_id="client-id",
                      refresh_token="refresh-token",
                      scopes=None,
                      **kwargs):
    if authority:
        endpoint = "https://" + "/".join((
            authority,
            utid,
            "path",
        ))
    else:
        endpoint = get_default_authority() + "/{}/{}".format(utid, "path")

    return {
        "response":
        build_aad_response(uid=uid,
                           utid=utid,
                           refresh_token=refresh_token,
                           id_token=build_id_token(aud=client_id,
                                                   username=username),
                           foci="1",
                           **kwargs),
        "client_id":
        client_id,
        "token_endpoint":
        endpoint,
        "scope":
        scopes or ["scope"],
    }
Exemplo n.º 3
0
def get_account_event(username,
                      uid,
                      utid,
                      authority=None,
                      client_id="client-id",
                      refresh_token="refresh-token",
                      scopes=None):
    return {
        "response":
        build_aad_response(
            uid=uid,
            utid=utid,
            refresh_token=refresh_token,
            id_token=build_id_token(aud=client_id,
                                    preferred_username=username),
            foci="1",
        ),
        "client_id":
        client_id,
        "token_endpoint":
        "https://" + "/".join(
            (authority or KnownAuthorities.AZURE_PUBLIC_CLOUD, utid, "/path")),
        "scope":
        scopes or ["scope"],
    }
def test_policies_configurable():
    policy = Mock(spec_set=SansIOHTTPPolicy, on_request=Mock())
    client_id = "client-id"
    transport = validating_transport(
        requests=[Request()] * 2,
        responses=[
            get_discovery_response(),
            mock_response(json_payload=build_aad_response(
                access_token="**", id_token=build_id_token(aud=client_id))),
        ],
    )

    # mock local server fakes successful authentication by immediately returning a well-formed response
    oauth_state = "oauth-state"
    auth_code_response = {"code": "authorization-code", "state": [oauth_state]}
    server_class = Mock(return_value=Mock(
        wait_for_redirect=lambda: auth_code_response))

    credential = InteractiveBrowserCredential(policies=[policy],
                                              client_id=client_id,
                                              transport=transport,
                                              _server_class=server_class,
                                              _cache=TokenCache())

    with patch("azure.identity._credentials.browser.uuid.uuid4",
               lambda: oauth_state):
        credential.get_token("scope")

    assert policy.on_request.called
Exemplo n.º 5
0
def test_user_agent():
    client_id = "client-id"
    transport = validating_transport(
        requests=[Request()] * 2 +
        [Request(required_headers={"User-Agent": USER_AGENT})],
        responses=[
            get_discovery_response(),
            mock_response(
                json_payload={
                    "device_code": "_",
                    "user_code": "user-code",
                    "verification_uri": "verification-uri",
                    "expires_in": 42,
                }),
            mock_response(json_payload=dict(build_aad_response(
                access_token="**", id_token=build_id_token(aud=client_id)),
                                            scope="scope")),
        ],
    )

    credential = DeviceCodeCredential(client_id=client_id,
                                      prompt_callback=Mock(),
                                      transport=transport)

    credential.get_token("scope")
def test_claims_challenge():
    """get_token should and authenticate pass any claims challenge to MSAL token acquisition APIs"""

    msal_acquire_token_result = dict(
        build_aad_response(access_token="**", id_token=build_id_token()),
        id_token_claims=id_token_claims("issuer", "subject", "audience", upn="upn"),
    )
    expected_claims = '{"access_token": {"essential": "true"}'

    transport = Mock(send=Mock(side_effect=Exception("this test mocks MSAL, so no request should be sent")))
    credential = UsernamePasswordCredential("client-id", "username", "password", transport=transport)
    with patch.object(UsernamePasswordCredential, "_get_app") as get_mock_app:
        msal_app = get_mock_app()
        msal_app.acquire_token_by_username_password.return_value = msal_acquire_token_result

        credential.authenticate(scopes=["scope"], claims=expected_claims)
        assert msal_app.acquire_token_by_username_password.call_count == 1
        args, kwargs = msal_app.acquire_token_by_username_password.call_args
        assert kwargs["claims_challenge"] == expected_claims

        credential.get_token("scope", claims=expected_claims)

        assert msal_app.acquire_token_by_username_password.call_count == 2
        args, kwargs = msal_app.acquire_token_by_username_password.call_args
        assert kwargs["claims_challenge"] == expected_claims

        msal_app.get_accounts.return_value = [{"home_account_id": credential._auth_record.home_account_id}]
        msal_app.acquire_token_silent_with_error.return_value = msal_acquire_token_result
        credential.get_token("scope", claims=expected_claims)

        assert msal_app.acquire_token_silent_with_error.call_count == 1
        args, kwargs = msal_app.acquire_token_silent_with_error.call_args
        assert kwargs["claims_challenge"] == expected_claims
Exemplo n.º 7
0
def test_login_hint():
    expected_username = "******"
    auth_code_response = {"code": "authorization-code", "state": ["..."]}
    server_class = Mock(return_value=Mock(
        wait_for_redirect=lambda: auth_code_response))
    transport = Mock(send=Mock(side_effect=Exception(
        "this test mocks MSAL, so no request should be sent")))

    msal_acquire_token_result = dict(
        build_aad_response(access_token="**", id_token=build_id_token()),
        id_token_claims=id_token_claims("issuer",
                                        "subject",
                                        "audience",
                                        upn="upn"),
    )
    mock_msal_app = Mock(
        acquire_token_by_auth_code_flow=Mock(
            return_value=msal_acquire_token_result),
        initiate_auth_code_flow=Mock(
            return_value={"auth_uri": "http://localhost"}),
    )

    credential = InteractiveBrowserCredential(_server_class=server_class,
                                              transport=transport,
                                              login_hint=expected_username)
    with patch("msal.PublicClientApplication",
               Mock(return_value=mock_msal_app)):
        with patch(WEBBROWSER_OPEN, lambda _: True):
            credential.authenticate(scopes=["scope"])

    assert mock_msal_app.initiate_auth_code_flow.call_count == 1
    _, kwargs = mock_msal_app.initiate_auth_code_flow.call_args
    assert kwargs["login_hint"] == expected_username
Exemplo n.º 8
0
def test_claims_challenge():
    """get_token should pass any claims challenge to MSAL token acquisition APIs"""

    expected_claims = '{"access_token": {"essential": "true"}'

    record = AuthenticationRecord("tenant-id", "client_id", "authority",
                                  "home_account_id", "username")

    msal_app = Mock()
    msal_app.get_accounts.return_value = [{
        "home_account_id":
        record.home_account_id
    }]
    msal_app.acquire_token_silent_with_error.return_value = dict(
        build_aad_response(access_token="**", id_token=build_id_token()))

    transport = Mock(send=Mock(side_effect=Exception(
        "this test mocks MSAL, so no request should be sent")))
    credential = SharedTokenCacheCredential(transport=transport,
                                            authentication_record=record,
                                            _cache=TokenCache())
    with patch(
            SharedTokenCacheCredential.__module__ + ".PublicClientApplication",
            lambda *_, **__: msal_app):
        credential.get_token("scope", claims=expected_claims)

    assert msal_app.acquire_token_silent_with_error.call_count == 1
    args, kwargs = msal_app.acquire_token_silent_with_error.call_args
    assert kwargs["claims_challenge"] == expected_claims
def test_device_code_credential():
    client_id = "client-id"
    expected_token = "access-token"
    user_code = "user-code"
    verification_uri = "verification-uri"
    expires_in = 42

    transport = validating_transport(
        requests=[Request()] *
        3,  # not validating requests because they're formed by MSAL
        responses=[
            # expected requests: discover tenant, start device code flow, poll for completion
            mock_response(
                json_payload={
                    "authorization_endpoint": "https://a/b",
                    "token_endpoint": "https://a/b"
                }),
            mock_response(
                json_payload={
                    "device_code": "_",
                    "user_code": user_code,
                    "verification_uri": verification_uri,
                    "expires_in": expires_in,
                }),
            mock_response(json_payload=dict(
                build_aad_response(
                    access_token=expected_token,
                    expires_in=expires_in,
                    refresh_token="_",
                    id_token=build_id_token(aud=client_id),
                ),
                scope="scope",
            ), ),
        ],
    )

    callback = Mock()
    credential = DeviceCodeCredential(
        client_id=client_id,
        prompt_callback=callback,
        transport=transport,
        instance_discovery=False,
        _cache=TokenCache(),
    )

    now = datetime.datetime.utcnow()
    token = credential.get_token("scope")
    assert token.token == expected_token

    # prompt_callback should have been called as documented
    assert callback.call_count == 1
    uri, code, expires_on = callback.call_args[0]
    assert uri == verification_uri
    assert code == user_code

    # validating expires_on exactly would require depending on internals of the credential and
    # patching time, so we'll be satisfied if expires_on is a datetime at least expires_in
    # seconds later than our call to get_token
    assert isinstance(expires_on, datetime.datetime)
    assert expires_on - now >= datetime.timedelta(seconds=expires_in)
def test_claims_challenge():
    """get_token should pass any claims challenge to MSAL token acquisition APIs"""

    msal_acquire_token_result = dict(
        build_aad_response(access_token="**", id_token=build_id_token()),
        id_token_claims=id_token_claims("issuer",
                                        "subject",
                                        "audience",
                                        upn="upn"),
    )
    expected_claims = '{"access_token": {"essential": "true"}'

    transport = Mock(send=Mock(side_effect=Exception(
        "this test mocks MSAL, so no request should be sent")))
    credential = DeviceCodeCredential(transport=transport)
    with patch.object(DeviceCodeCredential, "_get_app") as get_mock_app:
        msal_app = get_mock_app()
        msal_app.initiate_device_flow.return_value = {"message": "it worked"}
        msal_app.acquire_token_by_device_flow.return_value = msal_acquire_token_result
        credential.get_token("scope", claims=expected_claims)

        assert msal_app.acquire_token_by_device_flow.call_count == 1
        args, kwargs = msal_app.acquire_token_by_device_flow.call_args
        assert kwargs["claims_challenge"] == expected_claims

        msal_app.get_accounts.return_value = [{
            "home_account_id":
            credential._auth_record.home_account_id
        }]
        msal_app.acquire_token_silent_with_error.return_value = msal_acquire_token_result
        credential.get_token("scope", claims=expected_claims)

        assert msal_app.acquire_token_silent_with_error.call_count == 1
        args, kwargs = msal_app.acquire_token_silent_with_error.call_args
        assert kwargs["claims_challenge"] == expected_claims
def test_user_agent():
    client_id = "client-id"
    transport = validating_transport(
        requests=[
            Request(),
            Request(required_headers={"User-Agent": USER_AGENT})
        ],
        responses=[
            get_discovery_response(),
            mock_response(json_payload=build_aad_response(
                access_token="**", id_token=build_id_token(aud=client_id))),
        ],
    )

    # mock local server fakes successful authentication by immediately returning a well-formed response
    oauth_state = "oauth-state"
    auth_code_response = {"code": "authorization-code", "state": [oauth_state]}
    server_class = Mock(return_value=Mock(
        wait_for_redirect=lambda: auth_code_response))

    credential = InteractiveBrowserCredential(client_id=client_id,
                                              transport=transport,
                                              _server_class=server_class,
                                              _cache=TokenCache())

    with patch("azure.identity._credentials.browser.uuid.uuid4",
               lambda: oauth_state):
        credential.get_token("scope")
Exemplo n.º 12
0
def test_policies_configurable():
    policy = Mock(spec_set=SansIOHTTPPolicy, on_request=Mock())

    client_id = "client-id"
    transport = validating_transport(
        requests=[Request()] * 3,
        responses=[
            # expected requests: discover tenant, start device code flow, poll for completion
            get_discovery_response(),
            mock_response(
                json_payload={
                    "device_code": "_",
                    "user_code": "user-code",
                    "verification_uri": "verification-uri",
                    "expires_in": 42,
                }),
            mock_response(json_payload=dict(build_aad_response(
                access_token="**", id_token=build_id_token(aud=client_id)),
                                            scope="scope")),
        ],
    )

    credential = DeviceCodeCredential(client_id=client_id,
                                      prompt_callback=Mock(),
                                      policies=[policy],
                                      transport=transport)

    credential.get_token("scope")

    assert policy.on_request.called
def test_authenticate():
    client_id = "client-id"
    environment = "localhost"
    issuer = "https://" + environment
    tenant_id = "some-tenant"
    authority = issuer + "/" + tenant_id

    access_token = "***"
    scope = "scope"

    # mock AAD response with id token
    object_id = "object-id"
    home_tenant = "home-tenant-id"
    username = "******"
    id_token = build_id_token(aud=client_id,
                              iss=issuer,
                              object_id=object_id,
                              tenant_id=home_tenant,
                              username=username)
    auth_response = build_aad_response(uid=object_id,
                                       utid=home_tenant,
                                       access_token=access_token,
                                       refresh_token="**",
                                       id_token=id_token)

    transport = validating_transport(
        requests=[Request(url_substring=issuer)] * 4,
        responses=[get_discovery_response(authority)] * 2  # instance and tenant discovery
        + [
            mock_response(  # start device code flow
                json_payload={
                    "device_code": "_",
                    "user_code": "user-code",
                    "verification_uri": "verification-uri",
                    "expires_in": 42,
                }
            ),
            mock_response(json_payload=dict(auth_response, scope=scope)),  # poll for completion
        ],
    )

    credential = DeviceCodeCredential(
        client_id,
        prompt_callback=Mock(),  # prevent credential from printing to stdout
        transport=transport,
        authority=environment,
        tenant_id=tenant_id,
        _cache=TokenCache(),
    )
    record = credential.authenticate(scopes=(scope, ))

    # credential should have a cached access token for the scope used in authenticate
    token = credential.get_token(scope)
    assert token.token == access_token

    assert record.authority == environment
    assert record.home_account_id == object_id + "." + home_tenant
    assert record.tenant_id == home_tenant
    assert record.username == username
def test_user_agent():
    transport = validating_transport(
        requests=[Request()] * 2 + [Request(required_headers={"User-Agent": USER_AGENT})],
        responses=[get_discovery_response()] * 2
        + [mock_response(json_payload=build_aad_response(access_token="**", id_token=build_id_token()))],
    )

    credential = UsernamePasswordCredential("client-id", "username", "password", transport=transport)

    credential.get_token("scope")
Exemplo n.º 15
0
def test_writes_to_cache():
    """the credential should write tokens it acquires to the cache"""

    scope = "scope"
    expected_access_token = "access token"
    first_refresh_token = "first refresh token"
    second_refresh_token = "second refresh token"

    username = "******"
    uid = "uid"
    utid = "utid"
    account = get_account_event(username=username, uid=uid, utid=utid, refresh_token=first_refresh_token)
    cache = TokenCache()
    cache.add(account)

    transport = validating_transport(
        requests=[Request(required_data={"refresh_token": first_refresh_token})],  # credential redeems refresh token
        responses=[
            mock_response(
                json_payload=build_aad_response(  # AAD responds with an access token and new refresh token
                    uid=uid,
                    utid=utid,
                    access_token=expected_access_token,
                    refresh_token=second_refresh_token,
                    id_token=build_id_token(
                        aud=DEVELOPER_SIGN_ON_CLIENT_ID, object_id=uid, tenant_id=utid, username=username
                    ),
                )
            )
        ],
    )
    credential = SharedTokenCacheCredential(_cache=cache, transport=transport)
    token = credential.get_token(scope)
    assert token.token == expected_access_token

    # access token should be in the cache, and another instance should retrieve it
    credential = SharedTokenCacheCredential(
        _cache=cache, transport=Mock(send=Mock(side_effect=Exception("the credential should return a cached token")))
    )
    token = credential.get_token(scope)
    assert token.token == expected_access_token

    # and the credential should have updated the cached refresh token
    second_access_token = "second access token"
    transport = validating_transport(
        requests=[Request(required_data={"refresh_token": second_refresh_token})],
        responses=[mock_response(json_payload=build_aad_response(access_token=second_access_token))],
    )
    credential = SharedTokenCacheCredential(_cache=cache, transport=transport)
    token = credential.get_token("some other " + scope)
    assert token.token == second_access_token

    # verify the credential didn't add a new cache entry
    assert len(cache.find(TokenCache.CredentialType.REFRESH_TOKEN)) == 1
def test_authenticate():
    client_id = "client-id"
    environment = "localhost"
    issuer = "https://" + environment
    tenant_id = "some-tenant"
    authority = issuer + "/" + tenant_id

    access_token = "***"
    scope = "scope"

    # mock AAD response with id token
    object_id = "object-id"
    home_tenant = "home-tenant-id"
    username = "******"
    id_token = build_id_token(aud=client_id,
                              iss=issuer,
                              object_id=object_id,
                              tenant_id=home_tenant,
                              username=username)
    auth_response = build_aad_response(uid=object_id,
                                       utid=home_tenant,
                                       access_token=access_token,
                                       refresh_token="**",
                                       id_token=id_token)

    transport = validating_transport(
        requests=[Request(url_substring=issuer)] * 4,
        responses=[
            get_discovery_response(authority),  # instance discovery
            get_discovery_response(authority),  # tenant discovery
            mock_response(status_code=404),  # user realm discovery
            mock_response(json_payload=auth_response
                          ),  # token request following authenticate()
        ],
    )

    credential = UsernamePasswordCredential(
        username=username,
        password="******",
        authority=environment,
        client_id=client_id,
        tenant_id=tenant_id,
        transport=transport,
    )
    record = credential.authenticate(scopes=(scope, ))
    for auth_record in (record, credential.authentication_record):
        assert auth_record.authority == environment
        assert auth_record.home_account_id == object_id + "." + home_tenant
        assert auth_record.tenant_id == home_tenant
        assert auth_record.username == username

    # credential should have a cached access token for the scope passed to authenticate
    token = credential.get_token(scope)
    assert token.token == access_token
def test_claims_challenge():
    """get_token and authenticate should pass any claims challenge to MSAL token acquisition APIs"""

    expected_claims = '{"access_token": {"essential": "true"}'

    auth_code_response = {"code": "authorization-code", "state": ["..."]}
    server_class = Mock(return_value=Mock(
        wait_for_redirect=lambda: auth_code_response))

    msal_acquire_token_result = dict(
        build_aad_response(access_token="**", id_token=build_id_token()),
        id_token_claims=id_token_claims("issuer",
                                        "subject",
                                        "audience",
                                        upn="upn"),
    )

    transport = Mock(send=Mock(side_effect=Exception(
        "this test mocks MSAL, so no request should be sent")))
    credential = InteractiveBrowserCredential(_server_class=server_class,
                                              transport=transport)
    with patch.object(InteractiveBrowserCredential,
                      "_get_app") as get_mock_app:
        msal_app = get_mock_app()
        msal_app.initiate_auth_code_flow.return_value = {
            "auth_uri": "http://localhost"
        }
        msal_app.acquire_token_by_auth_code_flow.return_value = msal_acquire_token_result

        with patch(WEBBROWSER_OPEN, lambda _: True):
            credential.authenticate(scopes=["scope"], claims=expected_claims)

        assert msal_app.acquire_token_by_auth_code_flow.call_count == 1
        args, kwargs = msal_app.acquire_token_by_auth_code_flow.call_args
        assert kwargs["claims_challenge"] == expected_claims

        with patch(WEBBROWSER_OPEN, lambda _: True):
            credential.get_token("scope", claims=expected_claims)

        assert msal_app.acquire_token_by_auth_code_flow.call_count == 2
        args, kwargs = msal_app.acquire_token_by_auth_code_flow.call_args
        assert kwargs["claims_challenge"] == expected_claims

        msal_app.get_accounts.return_value = [{
            "home_account_id":
            credential._auth_record.home_account_id
        }]
        msal_app.acquire_token_silent_with_error.return_value = msal_acquire_token_result
        credential.get_token("scope", claims=expected_claims)

        assert msal_app.acquire_token_silent_with_error.call_count == 1
        args, kwargs = msal_app.acquire_token_silent_with_error.call_args
        assert kwargs["claims_challenge"] == expected_claims
def test_policies_configurable():
    policy = Mock(spec_set=SansIOHTTPPolicy, on_request=Mock())

    transport = validating_transport(
        requests=[Request()] * 3,
        responses=[get_discovery_response()] * 2
        + [mock_response(json_payload=build_aad_response(access_token="**", id_token=build_id_token()))],
    )
    credential = UsernamePasswordCredential("client-id", "username", "password", policies=[policy], transport=transport)

    credential.get_token("scope")

    assert policy.on_request.called
def test_authenticate():
    client_id = "client-id"
    environment = "localhost"
    issuer = "https://" + environment
    tenant_id = "some-tenant"
    authority = issuer + "/" + tenant_id

    access_token = "***"
    scope = "scope"

    # mock AAD response with id token
    object_id = "object-id"
    home_tenant = "home-tenant-id"
    username = "******"
    id_token = build_id_token(aud=client_id, iss=issuer, object_id=object_id, tenant_id=home_tenant, username=username)
    auth_response = build_aad_response(
        uid=object_id, utid=home_tenant, access_token=access_token, refresh_token="**", id_token=id_token
    )

    transport = validating_transport(
        requests=[Request(url_substring=issuer)] * 3,
        responses=[get_discovery_response(authority)] * 2 + [mock_response(json_payload=auth_response)],
    )

    # mock local server fakes successful authentication by immediately returning a well-formed response
    oauth_state = "state"
    auth_code_response = {"code": "authorization-code", "state": [oauth_state]}
    server_class = Mock(return_value=Mock(wait_for_redirect=lambda: auth_code_response))

    with patch(InteractiveBrowserCredential.__module__ + ".uuid.uuid4", lambda: oauth_state):
        with patch(WEBBROWSER_OPEN, lambda _: True):
            credential = InteractiveBrowserCredential(
                _cache=TokenCache(),
                authority=environment,
                client_id=client_id,
                server_class=server_class,
                tenant_id=tenant_id,
                transport=transport,
            )
            record = credential._authenticate(scopes=(scope,))

    assert record.authority == environment
    assert record.home_account_id == object_id + "." + home_tenant
    assert record.tenant_id == home_tenant
    assert record.username == username

    # credential should have a cached access token for the scope used in authenticate
    with patch(WEBBROWSER_OPEN, Mock(side_effect=Exception("credential should authenticate silently"))):
        token = credential.get_token(scope)
    assert token.token == access_token
def test_wsl_fallback(uname, is_wsl):
    """the credential should invoke powershell.exe to open a browser in WSL when webbrowser.open fails"""

    auth_uri = "http://localhost"
    expected_access_token = "**"
    msal_acquire_token_result = dict(
        build_aad_response(access_token=expected_access_token,
                           id_token=build_id_token()),
        id_token_claims=id_token_claims("issuer",
                                        "subject",
                                        "audience",
                                        upn="upn"),
    )
    msal_app = Mock(
        initiate_auth_code_flow=Mock(return_value={"auth_uri": auth_uri}),
        acquire_token_by_auth_code_flow=Mock(
            return_value=msal_acquire_token_result),
    )

    transport = Mock(send=Mock(side_effect=Exception(
        "this test mocks MSAL, so no request should be sent")))
    credential = InteractiveBrowserCredential(_server_class=Mock(),
                                              transport=transport)

    with patch(InteractiveBrowserCredential.__module__ +
               ".subprocess.call") as subprocess_call:
        subprocess_call.return_value = 0
        with patch(InteractiveBrowserCredential.__module__ + ".platform.uname",
                   lambda: uname):
            with patch.object(InteractiveBrowserCredential, "_get_app",
                              lambda _: msal_app):
                with patch(WEBBROWSER_OPEN, lambda _: False):
                    try:
                        token = credential.get_token("scope")
                    except CredentialUnavailableError:
                        assert not is_wsl, "credential should invoke powershell.exe in WSL"
                        return

    assert is_wsl, "credential should raise CredentialUnavailableError when not in WSL"
    assert token.token == expected_access_token
    assert subprocess_call.call_count == 1
    args, kwargs = subprocess_call.call_args
    assert args[0][0] == "powershell.exe"
    assert auth_uri in args[0][-1]
    if platform.python_version() >= "3.3":
        assert "timeout" in kwargs
Exemplo n.º 21
0
def test_tenant_id():
    client_id = "client-id"
    expected_token = "access-token"
    user_code = "user-code"
    verification_uri = "verification-uri"
    expires_in = 42

    transport = validating_transport(
        requests=[Request()] * 3,  # not validating requests because they're formed by MSAL
        responses=[
            # expected requests: discover tenant, start device code flow, poll for completion
            mock_response(json_payload={"authorization_endpoint": "https://a/b", "token_endpoint": "https://a/b"}),
            mock_response(
                json_payload={
                    "device_code": "_",
                    "user_code": user_code,
                    "verification_uri": verification_uri,
                    "expires_in": expires_in,
                }
            ),
            mock_response(
                json_payload=dict(
                    build_aad_response(
                        access_token=expected_token,
                        expires_in=expires_in,
                        refresh_token="_",
                        id_token=build_id_token(aud=client_id),
                    ),
                    scope="scope",
                ),
            ),
        ],
    )

    callback = Mock()
    credential = DeviceCodeCredential(
        client_id=client_id, prompt_callback=callback, transport=transport, instance_discovery=False,
    )

    now = datetime.datetime.utcnow()
    token = credential.get_token("scope", tenant_id="tenant_id")
    assert token.token == expected_token
def test_interactive_credential(mock_open):
    mock_open.side_effect = _validate_auth_request_url
    oauth_state = "state"
    client_id = "client-id"
    expected_refresh_token = "refresh-token"
    expected_token = "access-token"
    expires_in = 3600
    authority = "authority"
    tenant_id = "tenant_id"
    endpoint = "https://{}/{}".format(authority, tenant_id)

    discovery_response = get_discovery_response(endpoint=endpoint)
    transport = validating_transport(
        requests=[Request(url_substring=endpoint)] * 3
        + [
            Request(
                authority=authority, url_substring=endpoint, required_data={"refresh_token": expected_refresh_token}
            )
        ],
        responses=[
            discovery_response,  # instance discovery
            discovery_response,  # tenant discovery
            mock_response(
                json_payload=build_aad_response(
                    access_token=expected_token,
                    expires_in=expires_in,
                    refresh_token=expected_refresh_token,
                    uid="uid",
                    utid=tenant_id,
                    id_token=build_id_token(aud=client_id, object_id="uid", tenant_id=tenant_id, iss=endpoint),
                    token_type="Bearer",
                )
            ),
            mock_response(
                json_payload=build_aad_response(access_token=expected_token, expires_in=expires_in, token_type="Bearer")
            ),
        ],
    )

    # mock local server fakes successful authentication by immediately returning a well-formed response
    auth_code_response = {"code": "authorization-code", "state": [oauth_state]}
    server_class = Mock(return_value=Mock(wait_for_redirect=lambda: auth_code_response))

    credential = InteractiveBrowserCredential(
        authority=authority,
        tenant_id=tenant_id,
        client_id=client_id,
        server_class=server_class,
        transport=transport,
        instance_discovery=False,
        validate_authority=False,
        _cache=TokenCache(),
    )

    # The credential's auth code request includes a uuid which must be included in the redirect. Patching to
    # set the uuid requires less code here than a proper mock server.
    with patch("azure.identity._credentials.browser.uuid.uuid4", lambda: oauth_state):
        token = credential.get_token("scope")
    assert token.token == expected_token
    assert mock_open.call_count == 1

    # token should be cached, get_token shouldn't prompt again
    token = credential.get_token("scope")
    assert token.token == expected_token
    assert mock_open.call_count == 1

    # As of MSAL 1.0.0, applications build a new client every time they redeem a refresh token.
    # Here we patch the private method they use for the sake of test coverage.
    # TODO: this will probably break when this MSAL behavior changes
    app = credential._get_app()
    app._build_client = lambda *_: app.client  # pylint:disable=protected-access
    now = time.time()

    # expired access token -> credential should use refresh token instead of prompting again
    with patch("time.time", lambda: now + expires_in):
        token = credential.get_token("scope")
    assert token.token == expected_token
    assert mock_open.call_count == 1

    # ensure all expected requests were sent
    assert transport.send.call_count == 4
def test_interactive_credential(mock_open, redirect_url):
    mock_open.side_effect = _validate_auth_request_url
    oauth_state = "state"
    client_id = "client-id"
    expected_refresh_token = "refresh-token"
    expected_token = "access-token"
    expires_in = 3600
    authority = "authority"
    tenant_id = "tenant-id"
    endpoint = "https://{}/{}".format(authority, tenant_id)

    transport = msal_validating_transport(
        endpoint="https://{}/{}".format(authority, tenant_id),
        requests=[Request(url_substring=endpoint)] + [
            Request(authority=authority,
                    url_substring=endpoint,
                    required_data={"refresh_token": expected_refresh_token})
        ],
        responses=[
            mock_response(json_payload=build_aad_response(
                access_token=expected_token,
                expires_in=expires_in,
                refresh_token=expected_refresh_token,
                uid="uid",
                utid=tenant_id,
                id_token=build_id_token(aud=client_id,
                                        object_id="uid",
                                        tenant_id=tenant_id,
                                        iss=endpoint),
                token_type="Bearer",
            )),
            mock_response(
                json_payload=build_aad_response(access_token=expected_token,
                                                expires_in=expires_in,
                                                token_type="Bearer")),
        ],
    )

    # mock local server fakes successful authentication by immediately returning a well-formed response
    auth_code_response = {"code": "authorization-code", "state": [oauth_state]}
    server_class = Mock(return_value=Mock(
        wait_for_redirect=lambda: auth_code_response))

    args = {
        "authority": authority,
        "tenant_id": tenant_id,
        "client_id": client_id,
        "transport": transport,
        "_cache": TokenCache(),
        "_server_class": server_class,
    }
    if redirect_url:  # avoid passing redirect_url=None
        args["redirect_uri"] = redirect_url

    credential = InteractiveBrowserCredential(**args)

    # The credential's auth code request includes a uuid which must be included in the redirect. Patching to
    # set the uuid requires less code here than a proper mock server.
    with patch("azure.identity._credentials.browser.uuid.uuid4",
               lambda: oauth_state):
        token = credential.get_token("scope")
    assert token.token == expected_token
    assert mock_open.call_count == 1
    assert server_class.call_count == 1

    if redirect_url:
        server_class.assert_called_once_with(redirect_url, timeout=ANY)

    # token should be cached, get_token shouldn't prompt again
    token = credential.get_token("scope")
    assert token.token == expected_token
    assert mock_open.call_count == 1
    assert server_class.call_count == 1

    # expired access token -> credential should use refresh token instead of prompting again
    now = time.time()
    with patch("time.time", lambda: now + expires_in):
        token = credential.get_token("scope")
    assert token.token == expected_token
    assert mock_open.call_count == 1

    # ensure all expected requests were sent
    assert transport.send.call_count == 4