|
41 | 41 | VerificationOptions, |
42 | 42 | error, |
43 | 43 | ) |
| 44 | +from tankersdk.experimental import authenticate_with_idp |
44 | 45 |
|
45 | 46 |
|
46 | 47 | def encode(string: str) -> str: |
@@ -68,6 +69,7 @@ def read_test_config() -> Dict[str, Any]: |
68 | 69 | "clientSecret": assert_env("TANKER_OIDC_CLIENT_SECRET"), |
69 | 70 | "provider": assert_env("TANKER_OIDC_PROVIDER"), |
70 | 71 | "issuer": assert_env("TANKER_OIDC_ISSUER"), |
| 72 | + "fakeOidcIssuerUrl": assert_env("TANKER_FAKE_OIDC_URL") + "/issuer", |
71 | 73 | } |
72 | 74 | res["oidc"]["users"] = { |
73 | 75 | "martine": { |
@@ -1546,6 +1548,24 @@ def set_up_oidc(app: Dict[str, str], admin: Admin) -> Dict[str, str]: |
1546 | 1548 | return cast(Dict[str, str], admin.get_app(app["id"])["oidc_providers"][0]) |
1547 | 1549 |
|
1548 | 1550 |
|
| 1551 | +def set_up_fake_oidc(app: Dict[str, str], admin: Admin) -> Dict[str, str]: |
| 1552 | + fake_oidc_issuer_url = TEST_CONFIG["oidc"]["fakeOidcIssuerUrl"] |
| 1553 | + |
| 1554 | + oidc_issuer = fake_oidc_issuer_url |
| 1555 | + oidc_client_id = "tanker" |
| 1556 | + oidc_provider = "fake-oidc" |
| 1557 | + admin.update_app( |
| 1558 | + app["id"], |
| 1559 | + oidc_providers=[ |
| 1560 | + AppOidcProvider( |
| 1561 | + client_id=oidc_client_id, display_name=oidc_provider, issuer=oidc_issuer |
| 1562 | + ) |
| 1563 | + ], |
| 1564 | + ) |
| 1565 | + |
| 1566 | + return cast(Dict[str, str], admin.get_app(app["id"])["oidc_providers"][0]) |
| 1567 | + |
| 1568 | + |
1549 | 1569 | @pytest.mark.asyncio |
1550 | 1570 | async def test_oidc_verification( |
1551 | 1571 | tmp_path: Path, app: Dict[str, str], admin: Admin |
@@ -1584,6 +1604,61 @@ async def test_oidc_verification( |
1584 | 1604 | await martine_laptop.stop() |
1585 | 1605 |
|
1586 | 1606 |
|
| 1607 | +@pytest.mark.asyncio |
| 1608 | +async def test_oidc_authorization_code_verification( |
| 1609 | + tmp_path: Path, app: Dict[str, str], admin: Admin |
| 1610 | +) -> None: |
| 1611 | + provider_config = set_up_fake_oidc(app, admin) |
| 1612 | + provider_id = provider_config["id"] |
| 1613 | + subject_cookie = "fake_oidc_subject=martine" |
| 1614 | + |
| 1615 | + phone_path = tmp_path / "phone" |
| 1616 | + phone_path.mkdir(exist_ok=True) |
| 1617 | + martine_phone = create_tanker(app["id"], persistent_path=phone_path) |
| 1618 | + identity = tankersdk_identity.create_identity( |
| 1619 | + app["id"], app["secret"], str(uuid.uuid4()) |
| 1620 | + ) |
| 1621 | + |
| 1622 | + await martine_phone.start(identity) |
| 1623 | + |
| 1624 | + verification1 = await authenticate_with_idp( |
| 1625 | + martine_phone, provider_id, subject_cookie |
| 1626 | + ) |
| 1627 | + verification2 = await authenticate_with_idp( |
| 1628 | + martine_phone, provider_id, subject_cookie |
| 1629 | + ) |
| 1630 | + await martine_phone.register_identity(verification1) |
| 1631 | + await martine_phone.stop() |
| 1632 | + |
| 1633 | + laptop_path = tmp_path / "laptop" |
| 1634 | + laptop_path.mkdir(exist_ok=True) |
| 1635 | + martine_laptop = create_tanker(app["id"], persistent_path=laptop_path) |
| 1636 | + await martine_laptop.start(identity) |
| 1637 | + |
| 1638 | + assert martine_laptop.status == TankerStatus.IDENTITY_VERIFICATION_NEEDED |
| 1639 | + await martine_laptop.verify_identity(verification2) |
| 1640 | + assert martine_laptop.status == TankerStatus.READY |
| 1641 | + |
| 1642 | + actual_methods = await martine_laptop.get_verification_methods() |
| 1643 | + (actual_method,) = actual_methods |
| 1644 | + assert actual_method.method_type == VerificationMethodType.OIDC_ID_TOKEN |
| 1645 | + |
| 1646 | + await martine_laptop.stop() |
| 1647 | + |
| 1648 | + tablet_path = tmp_path / "tablet" |
| 1649 | + tablet_path.mkdir(exist_ok=True) |
| 1650 | + martine_tablet = create_tanker(app["id"], persistent_path=tablet_path) |
| 1651 | + await martine_tablet.start(identity) |
| 1652 | + verification3 = await authenticate_with_idp( |
| 1653 | + martine_tablet, provider_id, "fake_oidc_subject=not_martine" |
| 1654 | + ) |
| 1655 | + |
| 1656 | + assert martine_tablet.status == TankerStatus.IDENTITY_VERIFICATION_NEEDED |
| 1657 | + with pytest.raises(error.InvalidVerification): |
| 1658 | + await martine_tablet.verify_identity(verification3) |
| 1659 | + await martine_tablet.stop() |
| 1660 | + |
| 1661 | + |
1587 | 1662 | @pytest.mark.asyncio |
1588 | 1663 | async def test_register_fails_with_preverified_email( |
1589 | 1664 | tmp_path: Path, app: Dict[str, str], admin: Admin |
@@ -1886,6 +1961,53 @@ async def test_set_verification_method_with_oidc( |
1886 | 1961 | await phone_tanker.stop() |
1887 | 1962 |
|
1888 | 1963 |
|
| 1964 | +@pytest.mark.asyncio |
| 1965 | +async def test_set_oidc_authorization_code_verification( |
| 1966 | + tmp_path: Path, app: Dict[str, str], admin: Admin |
| 1967 | +) -> None: |
| 1968 | + provider_config = set_up_fake_oidc(app, admin) |
| 1969 | + provider_id = provider_config["id"] |
| 1970 | + subject_cookie = "fake_oidc_subject=alice" |
| 1971 | + passphrase = "The cake is not a lie" |
| 1972 | + |
| 1973 | + laptop_path = tmp_path / "laptop" |
| 1974 | + laptop_path.mkdir(exist_ok=True) |
| 1975 | + laptop_tanker = create_tanker(app["id"], persistent_path=laptop_path) |
| 1976 | + alice_identity = tankersdk_identity.create_identity( |
| 1977 | + app["id"], app["secret"], str(uuid.uuid4()) |
| 1978 | + ) |
| 1979 | + |
| 1980 | + await laptop_tanker.start(alice_identity) |
| 1981 | + await laptop_tanker.register_identity(PassphraseVerification(passphrase)) |
| 1982 | + |
| 1983 | + verification2 = await authenticate_with_idp( |
| 1984 | + laptop_tanker, provider_id, subject_cookie |
| 1985 | + ) |
| 1986 | + await laptop_tanker.set_verification_method(verification2) |
| 1987 | + |
| 1988 | + methods = set(await laptop_tanker.get_verification_methods()) |
| 1989 | + assert len(methods) == 2 |
| 1990 | + oidc_methods = [x for x in methods if isinstance(x, OidcIdTokenVerificationMethod)] |
| 1991 | + assert oidc_methods[0].provider_id == provider_config["id"] |
| 1992 | + assert oidc_methods[0].provider_display_name == provider_config["display_name"] |
| 1993 | + |
| 1994 | + phone_path = tmp_path.joinpath("phone") |
| 1995 | + phone_path.mkdir(exist_ok=True) |
| 1996 | + phone_tanker = create_tanker(app["id"], persistent_path=phone_path) |
| 1997 | + |
| 1998 | + await phone_tanker.start(alice_identity) |
| 1999 | + assert phone_tanker.status == TankerStatus.IDENTITY_VERIFICATION_NEEDED |
| 2000 | + |
| 2001 | + verification2 = await authenticate_with_idp( |
| 2002 | + laptop_tanker, provider_id, subject_cookie |
| 2003 | + ) |
| 2004 | + await phone_tanker.verify_identity(verification2) |
| 2005 | + assert phone_tanker.status == TankerStatus.READY |
| 2006 | + |
| 2007 | + await laptop_tanker.stop() |
| 2008 | + await phone_tanker.stop() |
| 2009 | + |
| 2010 | + |
1889 | 2011 | def test_prehash_password_empty() -> None: |
1890 | 2012 | with pytest.raises(error.InvalidArgument): |
1891 | 2013 | tankersdk.prehash_password("") |
|
0 commit comments