2
* Copyright (C) 2019 Apple Inc. All rights reserved.
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions
7
* 1. Redistributions of source code must retain the above copyright
8
* notice, this list of conditions and the following disclaimer.
9
* 2. Redistributions in binary form must reproduce the above copyright
10
* notice, this list of conditions and the following disclaimer in the
11
* documentation and/or other materials provided with the distribution.
13
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23
* THE POSSIBILITY OF SUCH DAMAGE.
28
#include <WebCore/LoggedInStatus.h>
29
#include <WebCore/RegistrableDomain.h>
30
#include <wtf/Seconds.h>
31
#include <wtf/text/StringBuilder.h>
33
using namespace WebCore;
35
namespace TestWebKitAPI {
37
// Positive test cases.
39
TEST(LoggedInStatus, DefaultExpiryWebAuthn)
41
RegistrableDomain loginDomain { URL { { }, "https://login.example.com"_s } };
43
auto loggedInResult = LoggedInStatus::create(loginDomain, "admin"_s, LoggedInStatus::CredentialTokenType::HTTPStateToken, LoggedInStatus::AuthenticationType::WebAuthn);
44
auto& loggedIn = loggedInResult.releaseReturnValue().get();
45
auto now = WallTime::now();
46
ASSERT_EQ(loggedIn.registrableDomain().string(), "example.com"_s);
47
ASSERT_EQ(loggedIn.username(), "admin"_s);
48
ASSERT_EQ(loggedIn.credentialTokenType(), LoggedInStatus::CredentialTokenType::HTTPStateToken);
49
ASSERT_EQ(loggedIn.authenticationType(), LoggedInStatus::AuthenticationType::WebAuthn);
50
ASSERT_TRUE(loggedIn.loggedInTime() < now + 60_s);
51
ASSERT_TRUE(loggedIn.loggedInTime() > now - 60_s);
52
ASSERT_TRUE(loggedIn.expiry() < now + LoggedInStatus::TimeToLiveLong + 60_s);
53
ASSERT_TRUE(loggedIn.expiry() > now + LoggedInStatus::TimeToLiveLong - 60_s);
54
ASSERT_FALSE(loggedIn.hasExpired());
57
TEST(LoggedInStatus, DefaultExpiryPasswordManager)
59
RegistrableDomain loginDomain { URL { { }, "https://login.example.com"_s } };
61
auto loggedInResult = LoggedInStatus::create(loginDomain, "admin"_s, LoggedInStatus::CredentialTokenType::HTTPStateToken, LoggedInStatus::AuthenticationType::PasswordManager);
62
auto& loggedIn = loggedInResult.releaseReturnValue().get();
63
auto now = WallTime::now();
64
ASSERT_EQ(loggedIn.authenticationType(), LoggedInStatus::AuthenticationType::PasswordManager);
65
ASSERT_TRUE(loggedIn.expiry() < now + LoggedInStatus::TimeToLiveLong + 60_s);
66
ASSERT_TRUE(loggedIn.expiry() > now + LoggedInStatus::TimeToLiveLong - 60_s);
67
ASSERT_FALSE(loggedIn.hasExpired());
70
TEST(LoggedInStatus, DefaultExpiryUnmanaged)
72
RegistrableDomain loginDomain { URL { { }, "https://login.example.com"_s } };
74
auto loggedInResult = LoggedInStatus::create(loginDomain, "admin"_s, LoggedInStatus::CredentialTokenType::HTTPStateToken, LoggedInStatus::AuthenticationType::Unmanaged);
75
auto& loggedIn = loggedInResult.releaseReturnValue().get();
76
auto now = WallTime::now();
77
ASSERT_EQ(loggedIn.authenticationType(), LoggedInStatus::AuthenticationType::Unmanaged);
78
ASSERT_TRUE(loggedIn.expiry() < now + LoggedInStatus::TimeToLiveShort + 60_s);
79
ASSERT_TRUE(loggedIn.expiry() > now + LoggedInStatus::TimeToLiveShort - 60_s);
80
ASSERT_FALSE(loggedIn.hasExpired());
83
TEST(LoggedInStatus, CustomExpiryBelowLong)
85
RegistrableDomain loginDomain { URL { { }, "https://login.example.com"_s } };
87
auto customExpiry = LoggedInStatus::TimeToLiveLong - 48_h;
88
auto loggedInResult = LoggedInStatus::create(loginDomain, "admin"_s, LoggedInStatus::CredentialTokenType::HTTPStateToken, LoggedInStatus::AuthenticationType::WebAuthn, customExpiry);
89
auto& loggedIn = loggedInResult.releaseReturnValue().get();
90
auto now = WallTime::now();
91
ASSERT_EQ(loggedIn.authenticationType(), LoggedInStatus::AuthenticationType::WebAuthn);
92
ASSERT_TRUE(loggedIn.expiry() < now + customExpiry + 60_s);
93
ASSERT_TRUE(loggedIn.expiry() > now + customExpiry - 60_s);
94
ASSERT_FALSE(loggedIn.hasExpired());
97
TEST(LoggedInStatus, CustomExpiryBelowShort)
99
RegistrableDomain loginDomain { URL { { }, "https://login.example.com"_s } };
101
auto customExpiry = LoggedInStatus::TimeToLiveShort - 48_h;
102
auto loggedInResult = LoggedInStatus::create(loginDomain, "admin"_s, LoggedInStatus::CredentialTokenType::HTTPStateToken, LoggedInStatus::AuthenticationType::PasswordManager, customExpiry);
103
auto& loggedIn = loggedInResult.releaseReturnValue().get();
104
auto now = WallTime::now();
105
ASSERT_EQ(loggedIn.authenticationType(), LoggedInStatus::AuthenticationType::PasswordManager);
106
ASSERT_TRUE(loggedIn.expiry() < now + customExpiry + 60_s);
107
ASSERT_TRUE(loggedIn.expiry() > now + customExpiry - 60_s);
108
ASSERT_FALSE(loggedIn.hasExpired());
111
TEST(LoggedInStatus, RenewedExpiry)
113
RegistrableDomain loginDomain { URL { { }, "https://login.example.com"_s } };
115
auto customExpiry = LoggedInStatus::TimeToLiveShort - 48_h;
116
auto loggedInResult = LoggedInStatus::create(loginDomain, "admin"_s, LoggedInStatus::CredentialTokenType::HTTPStateToken, LoggedInStatus::AuthenticationType::WebAuthn, customExpiry);
117
auto& loggedIn = loggedInResult.releaseReturnValue().get();
118
auto now = WallTime::now();
119
ASSERT_EQ(loggedIn.authenticationType(), LoggedInStatus::AuthenticationType::WebAuthn);
120
ASSERT_TRUE(loggedIn.expiry() < now + customExpiry + 60_s);
121
ASSERT_TRUE(loggedIn.expiry() > now + customExpiry - 60_s);
122
ASSERT_FALSE(loggedIn.hasExpired());
123
auto newCustomExpiry = 24_h;
124
loggedIn.setTimeToLive(newCustomExpiry);
125
ASSERT_EQ(loggedIn.authenticationType(), LoggedInStatus::AuthenticationType::WebAuthn);
126
ASSERT_TRUE(loggedIn.expiry() < now + newCustomExpiry + 60_s);
127
ASSERT_TRUE(loggedIn.expiry() > now + newCustomExpiry - 60_s);
128
ASSERT_FALSE(loggedIn.hasExpired());
131
// Negative test cases.
133
TEST(LoggedInStatus, InvalidUsernames)
135
RegistrableDomain loginDomain { URL { { }, "https://login.example.com"_s } };
137
// Whitespace is not allowed.
138
auto loggedInResult = LoggedInStatus::create(loginDomain, "I use spaces"_s, LoggedInStatus::CredentialTokenType::HTTPStateToken, LoggedInStatus::AuthenticationType::WebAuthn);
139
ASSERT_TRUE(loggedInResult.hasException());
141
// Newlines are not allowed.
142
loggedInResult = LoggedInStatus::create(loginDomain, "Enter\nreturn"_s, LoggedInStatus::CredentialTokenType::HTTPStateToken, LoggedInStatus::AuthenticationType::WebAuthn);
143
ASSERT_TRUE(loggedInResult.hasException());
145
// There is a max length to usernames.
146
StringBuilder builder;
147
for (unsigned i = 0; i <= LoggedInStatus::UsernameMaxLength; ++i)
149
loggedInResult = LoggedInStatus::create(loginDomain, builder.toString(), LoggedInStatus::CredentialTokenType::HTTPStateToken, LoggedInStatus::AuthenticationType::WebAuthn);
150
ASSERT_TRUE(loggedInResult.hasException());
153
TEST(LoggedInStatus, ClampedExpiryLong)
155
RegistrableDomain loginDomain { URL { { }, "https://login.example.com"_s } };
157
// Too long expiries for managed auth types should be clamped to LoggedInStatus::TimeToLiveLong.
158
auto loggedInResult = LoggedInStatus::create(loginDomain, "admin"_s, LoggedInStatus::CredentialTokenType::HTTPStateToken, LoggedInStatus::AuthenticationType::WebAuthn, LoggedInStatus::TimeToLiveLong + 24_h);
159
auto& loggedIn = loggedInResult.releaseReturnValue().get();
160
auto now = WallTime::now();
161
ASSERT_EQ(loggedIn.authenticationType(), LoggedInStatus::AuthenticationType::WebAuthn);
162
ASSERT_TRUE(loggedIn.expiry() < now + LoggedInStatus::TimeToLiveLong + 60_s);
163
ASSERT_TRUE(loggedIn.expiry() > now + LoggedInStatus::TimeToLiveLong - 60_s);
164
ASSERT_FALSE(loggedIn.hasExpired());
166
// Renewed, too long expiries for managed auth types should also be clamped to LoggedInStatus::TimeToLiveLong.
167
auto newCustomExpiry = LoggedInStatus::TimeToLiveLong + 24_h;
168
loggedIn.setTimeToLive(newCustomExpiry);
169
ASSERT_EQ(loggedIn.authenticationType(), LoggedInStatus::AuthenticationType::WebAuthn);
170
ASSERT_TRUE(loggedIn.expiry() < now + LoggedInStatus::TimeToLiveLong + 60_s);
171
ASSERT_TRUE(loggedIn.expiry() > now + LoggedInStatus::TimeToLiveLong - 60_s);
172
ASSERT_FALSE(loggedIn.hasExpired());
175
TEST(LoggedInStatus, ClampedExpiryShort)
177
RegistrableDomain loginDomain { URL { { }, "https://login.example.com"_s } };
179
// Too long expiries for unmanaged auth types should be clamped to LoggedInStatus::TimeToLiveShort.
180
auto loggedInResult = LoggedInStatus::create(loginDomain, "admin"_s, LoggedInStatus::CredentialTokenType::HTTPStateToken, LoggedInStatus::AuthenticationType::Unmanaged, LoggedInStatus::TimeToLiveShort + 24_h);
181
auto& loggedIn = loggedInResult.releaseReturnValue().get();
182
auto now = WallTime::now();
183
ASSERT_EQ(loggedIn.authenticationType(), LoggedInStatus::AuthenticationType::Unmanaged);
184
ASSERT_TRUE(loggedIn.expiry() < now + LoggedInStatus::TimeToLiveShort + 60_s);
185
ASSERT_TRUE(loggedIn.expiry() > now + LoggedInStatus::TimeToLiveShort - 60_s);
186
ASSERT_FALSE(loggedIn.hasExpired());
188
// Renewed, too long expiries for unmanaged auth types should also be clamped to LoggedInStatus::TimeToLiveShort.
189
auto newCustomExpiry = LoggedInStatus::TimeToLiveShort + 24_h;
190
loggedIn.setTimeToLive(newCustomExpiry);
191
ASSERT_EQ(loggedIn.authenticationType(), LoggedInStatus::AuthenticationType::Unmanaged);
192
ASSERT_TRUE(loggedIn.expiry() < now + LoggedInStatus::TimeToLiveShort + 60_s);
193
ASSERT_TRUE(loggedIn.expiry() > now + LoggedInStatus::TimeToLiveShort - 60_s);
194
ASSERT_FALSE(loggedIn.hasExpired());
197
} // namespace TestWebKitAPI