Audacity 3.2.0
Public Member Functions | Private Types | Private Member Functions | Private Attributes | List of all members
audacity::cloud::audiocom::OAuthService Class Referencefinal

Service responsible for OAuth authentication against the audio.com service. More...

#include <OAuthService.h>

Inheritance diagram for audacity::cloud::audiocom::OAuthService:
[legend]
Collaboration diagram for audacity::cloud::audiocom::OAuthService:
[legend]

Public Member Functions

bool HasAccessToken () const
 Indicates, that service has a valid access token, i. e. that the user is authorized. More...
 
bool HasRefreshToken () const
 
void ValidateAuth (std::function< void(std::string_view)> completedHandler, bool silent)
 Attempt to authorize the user. More...
 
bool HandleLinkURI (std::string_view uri, std::function< void(std::string_view)> completedHandler)
 Handle the OAuth callback. More...
 
void UnlinkAccount ()
 Removes access and refresh token, notifies about the logout. More...
 
std::string GetAccessToken () const
 Return the current access token, if any. More...
 
- Public Member Functions inherited from Observer::Publisher< AuthStateChangedMessage >
 Publisher (ExceptionPolicy *pPolicy=nullptr, Alloc a={})
 Constructor supporting type-erased custom allocation/deletion. More...
 
 Publisher (Publisher &&)=default
 
Publisheroperator= (Publisher &&)=default
 
Subscription Subscribe (Callback callback)
 Connect a callback to the Publisher; later-connected are called earlier. More...
 
Subscription Subscribe (Object &obj, Return(Object::*callback)(Args...))
 Overload of Subscribe takes an object and pointer-to-member-function. More...
 

Private Types

using Clock = std::chrono::steady_clock
 

Private Member Functions

void AuthorisePassword (const ServiceConfig &config, std::string_view userName, std::string_view password, std::function< void(std::string_view)> completedHandler)
 
void AuthoriseRefreshToken (const ServiceConfig &config, std::string_view refreshToken, std::function< void(std::string_view)> completedHandler, bool silent)
 
void AuthoriseRefreshToken (const ServiceConfig &config, std::function< void(std::string_view)> completedHandler, bool silent)
 
void AuthoriseCode (const ServiceConfig &config, std::string_view authorizationCode, std::function< void(std::string_view)> completedHandler)
 
void DoAuthorise (const ServiceConfig &config, std::string_view payload, std::function< void(std::string_view)> completedHandler, bool silent)
 
void SafePublish (const AuthStateChangedMessage &message)
 

Private Attributes

std::recursive_mutex mMutex
 
Clock::time_point mTokenExpirationTime
 
std::string mAccessToken
 

Additional Inherited Members

- Public Types inherited from Observer::Publisher< AuthStateChangedMessage >
using message_type = AuthStateChangedMessage
 
using CallbackReturn = std::conditional_t< true, void, bool >
 
using Callback = std::function< CallbackReturn(const AuthStateChangedMessage &) >
 Type of functions that can be connected to the Publisher. More...
 
- Static Public Attributes inherited from Observer::Publisher< AuthStateChangedMessage >
static constexpr bool notifies_all
 
- Protected Member Functions inherited from Observer::Publisher< AuthStateChangedMessage >
CallbackReturn Publish (const AuthStateChangedMessage &message)
 Send a message to connected callbacks. More...
 

Detailed Description

Service responsible for OAuth authentication against the audio.com service.

Definition at line 38 of file OAuthService.h.

Member Typedef Documentation

◆ Clock

using audacity::cloud::audiocom::OAuthService::Clock = std::chrono::steady_clock
private

Definition at line 107 of file OAuthService.h.

Member Function Documentation

◆ AuthoriseCode()

void audacity::cloud::audiocom::OAuthService::AuthoriseCode ( const ServiceConfig config,
std::string_view  authorizationCode,
std::function< void(std::string_view)>  completedHandler 
)
private

Definition at line 242 of file OAuthService.cpp.

245{
246 using namespace rapidjson;
247
248 Document document;
249 document.SetObject();
250
251 WriteCommonFields(document, "authorization_code", "all");
252
253 document.AddMember(
254 "code", StringRef(authorizationCode.data(), authorizationCode.size()),
255 document.GetAllocator());
256
257 const auto redirectURI = config.GetOAuthRedirectURL();
258
259 document.AddMember(
260 "redirect_uri", StringRef(redirectURI.data(), redirectURI.size()),
261 document.GetAllocator());
262
263 rapidjson::StringBuffer buffer;
264 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
265 document.Accept(writer);
266
268 config, { buffer.GetString(), buffer.GetSize() },
269 std::move(completedHandler), false);
270}
void DoAuthorise(const ServiceConfig &config, std::string_view payload, std::function< void(std::string_view)> completedHandler, bool silent)
void WriteCommonFields(rapidjson::Document &document, std::string_view grantType, std::string_view scope)

References DoAuthorise(), audacity::cloud::audiocom::ServiceConfig::GetOAuthRedirectURL(), and audacity::cloud::audiocom::anonymous_namespace{OAuthService.cpp}::WriteCommonFields().

Referenced by HandleLinkURI().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ AuthorisePassword()

void audacity::cloud::audiocom::OAuthService::AuthorisePassword ( const ServiceConfig config,
std::string_view  userName,
std::string_view  password,
std::function< void(std::string_view)>  completedHandler 
)
private

Definition at line 178 of file OAuthService.cpp.

182{
183 using namespace rapidjson;
184
185 Document document;
186 document.SetObject();
187
188 WriteCommonFields(document, "password", "all");
189
190 document.AddMember(
191 "username", StringRef(userName.data(), userName.size()),
192 document.GetAllocator());
193
194 document.AddMember(
195 "password", StringRef(password.data(), password.size()),
196 document.GetAllocator());
197
198 rapidjson::StringBuffer buffer;
199 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
200 document.Accept(writer);
201
203 config, { buffer.GetString(), buffer.GetSize() },
204 std::move(completedHandler), false);
205}

References DoAuthorise(), audacity::cloud::audiocom::anonymous_namespace{UserService.cpp}::userName, and audacity::cloud::audiocom::anonymous_namespace{OAuthService.cpp}::WriteCommonFields().

Referenced by HandleLinkURI().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ AuthoriseRefreshToken() [1/2]

void audacity::cloud::audiocom::OAuthService::AuthoriseRefreshToken ( const ServiceConfig config,
std::function< void(std::string_view)>  completedHandler,
bool  silent 
)
private

Definition at line 231 of file OAuthService.cpp.

234{
235 std::lock_guard<std::recursive_mutex> lock(mMutex);
236
239 std::move(completedHandler), silent);
240}
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:207
void AuthoriseRefreshToken(const ServiceConfig &config, std::string_view refreshToken, std::function< void(std::string_view)> completedHandler, bool silent)
std::string ToUTF8(const std::wstring &wstr)

References AuthoriseRefreshToken(), mMutex, Setting< T >::Read(), audacity::cloud::audiocom::anonymous_namespace{OAuthService.cpp}::refreshToken, and audacity::ToUTF8().

Here is the call graph for this function:

◆ AuthoriseRefreshToken() [2/2]

void audacity::cloud::audiocom::OAuthService::AuthoriseRefreshToken ( const ServiceConfig config,
std::string_view  refreshToken,
std::function< void(std::string_view)>  completedHandler,
bool  silent 
)
private

Definition at line 207 of file OAuthService.cpp.

210{
211 using namespace rapidjson;
212
213 Document document;
214 document.SetObject();
215
216 WriteCommonFields(document, "refresh_token", "");
217
218 document.AddMember(
219 "refresh_token", StringRef(token.data(), token.size()),
220 document.GetAllocator());
221
222 rapidjson::StringBuffer buffer;
223 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
224 document.Accept(writer);
225
227 config, { buffer.GetString(), buffer.GetSize() },
228 std::move(completedHandler), silent);
229}

References DoAuthorise(), and audacity::cloud::audiocom::anonymous_namespace{OAuthService.cpp}::WriteCommonFields().

Referenced by AuthoriseRefreshToken(), HandleLinkURI(), and ValidateAuth().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ DoAuthorise()

void audacity::cloud::audiocom::OAuthService::DoAuthorise ( const ServiceConfig config,
std::string_view  payload,
std::function< void(std::string_view)>  completedHandler,
bool  silent 
)
private

Definition at line 293 of file OAuthService.cpp.

296{
297 using namespace audacity::network_manager;
298
299 Request request(config.GetAPIUrl("/auth/token"));
300
301 request.setHeader(
303
304 request.setHeader(
306
307 auto response = NetworkManager::GetInstance().doPost(
308 request, payload.data(), payload.size());
309
310 response->setRequestFinishedCallback(
311 [response, this, handler = std::move(completedHandler), silent](auto)
312 {
313 const auto httpCode = response->getHTTPCode();
314 const auto body = response->readAll<std::string>();
315
316 if (httpCode != 200)
317 {
318 if (handler)
319 handler({});
320
321 // Token has expired?
322 if (httpCode == 422)
323 BasicUI::CallAfter([this] { UnlinkAccount(); });
324 else
325 SafePublish({ {}, body, false, silent });
326
327 return;
328 }
329
330 rapidjson::Document document;
331 document.Parse(body.data(), body.size());
332
333 if (!document.IsObject())
334 {
335 if (handler)
336 handler({});
337
338 SafePublish({ {}, body, false, silent });
339 return;
340 }
341
342 const auto tokenType = document["token_type"].GetString();
343 const auto accessToken = document["access_token"].GetString();
344 const auto expiresIn = document["expires_in"].GetInt64();
345 const auto newRefreshToken = document["refresh_token"].GetString();
346
347 {
348 std::lock_guard<std::recursive_mutex> lock(mMutex);
349
350 mAccessToken = std::string(tokenType) + " " + accessToken;
352 Clock::now() + std::chrono::seconds(expiresIn);
353 }
354
356 [token = std::string(newRefreshToken)]()
357 {
358 // At this point access token is already written,
359 // only refresh token is updated.
360 refreshToken.Write(token);
361 gPrefs->Flush();
362 });
363
364 if (handler)
366
367 // The callback only needs the access token, so invoke it immediately.
368 // Networking is thread safe
369 SafePublish({ mAccessToken, {}, true, silent });
370 });
371}
audacity::BasicSettings * gPrefs
Definition: Prefs.cpp:68
bool Write(const T &value)
Write value to config and return true if successful.
Definition: Prefs.h:259
virtual bool Flush() noexcept=0
void UnlinkAccount()
Removes access and refresh token, notifies about the logout.
void SafePublish(const AuthStateChangedMessage &message)
ResponsePtr doPost(const Request &request, const void *data, size_t size)
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
Definition: BasicUI.cpp:213

References audacity::network_manager::common_headers::Accept, audacity::network_manager::common_content_types::ApplicationJson, BasicUI::CallAfter(), audacity::network_manager::common_headers::ContentType, audacity::network_manager::NetworkManager::doPost(), audacity::BasicSettings::Flush(), audacity::cloud::audiocom::ServiceConfig::GetAPIUrl(), audacity::network_manager::NetworkManager::GetInstance(), gPrefs, audacity::cloud::audiocom::anonymous_namespace{AuthorizationHandler.cpp}::handler, mAccessToken, mMutex, mTokenExpirationTime, audacity::cloud::audiocom::anonymous_namespace{OAuthService.cpp}::refreshToken, SafePublish(), audacity::network_manager::Request::setHeader(), UnlinkAccount(), and Setting< T >::Write().

Referenced by AuthoriseCode(), AuthorisePassword(), and AuthoriseRefreshToken().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ GetAccessToken()

std::string audacity::cloud::audiocom::OAuthService::GetAccessToken ( ) const

Return the current access token, if any.

Definition at line 283 of file OAuthService.cpp.

284{
285 std::lock_guard<std::recursive_mutex> lock(mMutex);
286
287 if (Clock::now() < mTokenExpirationTime)
288 return mAccessToken;
289
290 return {};
291}

References mAccessToken, mMutex, and mTokenExpirationTime.

Referenced by HasAccessToken(), audacity::cloud::audiocom::sync::LocalProjectSnapshot::UpdateProjectSnapshot(), and ValidateAuth().

Here is the caller graph for this function:

◆ HandleLinkURI()

bool audacity::cloud::audiocom::OAuthService::HandleLinkURI ( std::string_view  uri,
std::function< void(std::string_view)>  completedHandler 
)

Handle the OAuth callback.

Definition at line 89 of file OAuthService.cpp.

91{
93 {
94 if (completedHandler)
95 completedHandler({});
96 return false;
97 }
98
99 // It was observed, that sometimes link is passed as audacity://link/
100 // This is valid from URI point of view, but we need to handle it separately
101 const auto argsStart = uri.find("?");
102
103 if (argsStart == std::string_view::npos)
104 {
105 if (completedHandler)
106 completedHandler({});
107 return false;
108 }
109
110 // Length is handled in IsPrefixed
111 auto args = uri.substr(argsStart + 1);
112
113 std::string_view token;
114 std::string_view username;
115 std::string_view password;
116 std::string_view authorizationCode;
117
118 while (!args.empty())
119 {
120 const auto nextArg = args.find('&');
121
122 const auto arg = args.substr(0, nextArg);
123 args = nextArg == std::string_view::npos ? "" : args.substr(nextArg + 1);
124
125 if (IsPrefixed(arg, usernamePrefix))
126 username = arg.substr(usernamePrefix.length());
127 else if (IsPrefixed(arg, passwordPrefix))
128 password = arg.substr(passwordPrefix.length());
129 else if (IsPrefixed(arg, tokenPrefix))
130 token = arg.substr(tokenPrefix.length());
131 else if (IsPrefixed(arg, authorizationCodePrefix))
132 authorizationCode = arg.substr(authorizationCodePrefix.length());
133 }
134
135 // We have a prioritized list of authorization methods
136 if (!authorizationCode.empty())
137 {
139 GetServiceConfig(), authorizationCode, std::move(completedHandler));
140 }
141 else if (!token.empty())
142 {
144 GetServiceConfig(), token, std::move(completedHandler), false);
145 }
146 else if (!username.empty() && !password.empty())
147 {
150 audacity::UrlDecode(std::string(username)),
151 audacity::UrlDecode(std::string(password)),
152 std::move(completedHandler));
153 }
154 else
155 {
156 if (completedHandler)
157 completedHandler({});
158
159 return false;
160 }
161
162 return true;
163}
bool IsPrefixedInsensitive(const HayType &hay, const PrefixType &prefix)
Definition: StringUtils.h:146
bool IsPrefixed(const HayType &hay, const PrefixType &prefix)
Definition: StringUtils.h:129
void AuthoriseCode(const ServiceConfig &config, std::string_view authorizationCode, std::function< void(std::string_view)> completedHandler)
void AuthorisePassword(const ServiceConfig &config, std::string_view userName, std::string_view password, std::function< void(std::string_view)> completedHandler)
constexpr size_t npos(-1)
const ServiceConfig & GetServiceConfig()
Returns the instance of the ServiceConfig.
std::string UrlDecode(const std::string &url)
Definition: UrlDecode.cpp:18

References AuthoriseCode(), AuthorisePassword(), AuthoriseRefreshToken(), audacity::cloud::audiocom::anonymous_namespace{OAuthService.cpp}::authorizationCodePrefix, audacity::cloud::audiocom::GetServiceConfig(), IsPrefixed(), IsPrefixedInsensitive(), Tuple::detail::npos(), audacity::cloud::audiocom::anonymous_namespace{OAuthService.cpp}::passwordPrefix, audacity::cloud::audiocom::anonymous_namespace{OAuthService.cpp}::tokenPrefix, audacity::cloud::audiocom::anonymous_namespace{OAuthService.cpp}::uriPrefix, audacity::UrlDecode(), and audacity::cloud::audiocom::anonymous_namespace{OAuthService.cpp}::usernamePrefix.

Referenced by audacity::cloud::audiocom::LinkWithTokenDialog::OnContinue().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ HasAccessToken()

bool audacity::cloud::audiocom::OAuthService::HasAccessToken ( ) const

Indicates, that service has a valid access token, i. e. that the user is authorized.

Definition at line 272 of file OAuthService.cpp.

273{
274 return !GetAccessToken().empty();
275}
std::string GetAccessToken() const
Return the current access token, if any.

References GetAccessToken().

Referenced by audacity::cloud::audiocom::anonymous_namespace{CloudSyncService.cpp}::PerformProjectGetRequest(), and ValidateAuth().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ HasRefreshToken()

bool audacity::cloud::audiocom::OAuthService::HasRefreshToken ( ) const

Indicates, that the service has a possibly valid refresh token, which can be used to authorize the user

Definition at line 277 of file OAuthService.cpp.

278{
279 std::lock_guard<std::recursive_mutex> lock(mMutex);
280 return !refreshToken.Read().empty();
281}

References mMutex, Setting< T >::Read(), and audacity::cloud::audiocom::anonymous_namespace{OAuthService.cpp}::refreshToken.

Referenced by ValidateAuth().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ SafePublish()

void audacity::cloud::audiocom::OAuthService::SafePublish ( const AuthStateChangedMessage message)
private

Definition at line 373 of file OAuthService.cpp.

374{
375 BasicUI::CallAfter([this, message]() { Publish(message); });
376}
CallbackReturn Publish(const AuthStateChangedMessage &message)
Send a message to connected callbacks.
Definition: Observer.h:207

References BasicUI::CallAfter(), and Observer::Publisher< AuthStateChangedMessage >::Publish().

Referenced by DoAuthorise().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ UnlinkAccount()

void audacity::cloud::audiocom::OAuthService::UnlinkAccount ( )

Removes access and refresh token, notifies about the logout.

Definition at line 165 of file OAuthService.cpp.

166{
167 std::lock_guard<std::recursive_mutex> lock(mMutex);
168
169 mAccessToken.clear();
171 gPrefs->Flush();
172
173 // Unlink account is expected to be called only
174 // on UI thread
175 Publish({ {}, {}, false });
176}

References audacity::BasicSettings::Flush(), gPrefs, mAccessToken, mMutex, Observer::Publisher< AuthStateChangedMessage >::Publish(), audacity::cloud::audiocom::anonymous_namespace{OAuthService.cpp}::refreshToken, and Setting< T >::Write().

Referenced by DoAuthorise(), audacity::cloud::audiocom::sync::ProjectCloudUIExtension::OnCloudStatusChanged(), and audacity::cloud::audiocom::anonymous_namespace{OAuthService.cpp}::OAuthServiceSettingsResetHandler::OnSettingResetEnd().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ValidateAuth()

void audacity::cloud::audiocom::OAuthService::ValidateAuth ( std::function< void(std::string_view)>  completedHandler,
bool  silent 
)

Attempt to authorize the user.

Definition at line 76 of file OAuthService.cpp.

78{
80 {
81 if (completedHandler)
82 completedHandler(GetAccessToken());
83 return;
84 }
85
86 AuthoriseRefreshToken(GetServiceConfig(), std::move(completedHandler), silent);
87}
bool HasAccessToken() const
Indicates, that service has a valid access token, i. e. that the user is authorized.

References AuthoriseRefreshToken(), GetAccessToken(), audacity::cloud::audiocom::GetServiceConfig(), HasAccessToken(), and HasRefreshToken().

Referenced by audacity::cloud::audiocom::UploadService::Upload().

Here is the call graph for this function:
Here is the caller graph for this function:

Member Data Documentation

◆ mAccessToken

std::string audacity::cloud::audiocom::OAuthService::mAccessToken
private

Definition at line 112 of file OAuthService.h.

Referenced by DoAuthorise(), GetAccessToken(), and UnlinkAccount().

◆ mMutex

std::recursive_mutex audacity::cloud::audiocom::OAuthService::mMutex
mutableprivate

◆ mTokenExpirationTime

Clock::time_point audacity::cloud::audiocom::OAuthService::mTokenExpirationTime
private

Definition at line 111 of file OAuthService.h.

Referenced by DoAuthorise(), and GetAccessToken().


The documentation for this class was generated from the following files: