diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/DotNetOAuth.Test/ChannelElements/OAuthChannelTests.cs | 9 | ||||
-rw-r--r-- | src/DotNetOAuth.Test/Scenarios/CoordinatingOAuthChannel.cs | 4 | ||||
-rw-r--r-- | src/DotNetOAuth.Test/Scenarios/Coordinator.cs | 4 | ||||
-rw-r--r-- | src/DotNetOAuth/ChannelElements/OAuthChannel.cs | 12 | ||||
-rw-r--r-- | src/DotNetOAuth/ChannelElements/OAuthConsumerMessageTypeProvider.cs | 108 | ||||
-rw-r--r-- | src/DotNetOAuth/ChannelElements/OAuthServiceProviderMessageTypeProvider.cs (renamed from src/DotNetOAuth/ChannelElements/OAuthMessageTypeProvider.cs) | 40 | ||||
-rw-r--r-- | src/DotNetOAuth/Consumer.cs | 33 | ||||
-rw-r--r-- | src/DotNetOAuth/DotNetOAuth.csproj | 3 | ||||
-rw-r--r-- | src/DotNetOAuth/Messaging/Channel.cs | 75 | ||||
-rw-r--r-- | src/DotNetOAuth/ServiceProvider.cs | 2 |
10 files changed, 212 insertions, 78 deletions
diff --git a/src/DotNetOAuth.Test/ChannelElements/OAuthChannelTests.cs b/src/DotNetOAuth.Test/ChannelElements/OAuthChannelTests.cs index 790a0d4..a5eebf2 100644 --- a/src/DotNetOAuth.Test/ChannelElements/OAuthChannelTests.cs +++ b/src/DotNetOAuth.Test/ChannelElements/OAuthChannelTests.cs @@ -52,8 +52,13 @@ namespace DotNetOAuth.Test.ChannelElements { }
[TestMethod]
- public void CtorSimple() {
- new OAuthChannel(this.signingElement, this.nonceStore, new InMemoryTokenManager());
+ public void CtorSimpleConsumer() {
+ new OAuthChannel(this.signingElement, this.nonceStore, new InMemoryTokenManager(), true);
+ }
+
+ [TestMethod]
+ public void CtorSimpleServiceProvider() {
+ new OAuthChannel(this.signingElement, this.nonceStore, new InMemoryTokenManager(), false);
}
[TestMethod]
diff --git a/src/DotNetOAuth.Test/Scenarios/CoordinatingOAuthChannel.cs b/src/DotNetOAuth.Test/Scenarios/CoordinatingOAuthChannel.cs index 5b74342..12974fd 100644 --- a/src/DotNetOAuth.Test/Scenarios/CoordinatingOAuthChannel.cs +++ b/src/DotNetOAuth.Test/Scenarios/CoordinatingOAuthChannel.cs @@ -29,11 +29,11 @@ namespace DotNetOAuth.Test.Scenarios { /// <param name="signingBindingElement">
/// The signing element for the Consumer to use. Null for the Service Provider.
/// </param>
- internal CoordinatingOAuthChannel(ITamperProtectionChannelBindingElement signingBindingElement)
+ internal CoordinatingOAuthChannel(ITamperProtectionChannelBindingElement signingBindingElement, bool isConsumer)
: base(
signingBindingElement,
new NonceMemoryStore(StandardExpirationBindingElement.DefaultMaximumMessageAge),
- new OAuthMessageTypeProvider(new InMemoryTokenManager()),
+ isConsumer ? (IMessageTypeProvider)new OAuthConsumerMessageTypeProvider(new InMemoryTokenManager()) : new OAuthServiceProviderMessageTypeProvider(new InMemoryTokenManager()),
new TestWebRequestHandler()) {
}
diff --git a/src/DotNetOAuth.Test/Scenarios/Coordinator.cs b/src/DotNetOAuth.Test/Scenarios/Coordinator.cs index ef0795e..ab270ce 100644 --- a/src/DotNetOAuth.Test/Scenarios/Coordinator.cs +++ b/src/DotNetOAuth.Test/Scenarios/Coordinator.cs @@ -51,8 +51,8 @@ namespace DotNetOAuth.Test.Scenarios { }
// Prepare channels that will pass messages directly back and forth.
- CoordinatingOAuthChannel consumerChannel = new CoordinatingOAuthChannel(this.SigningElement);
- CoordinatingOAuthChannel serviceProviderChannel = new CoordinatingOAuthChannel(this.SigningElement);
+ CoordinatingOAuthChannel consumerChannel = new CoordinatingOAuthChannel(this.SigningElement, true);
+ CoordinatingOAuthChannel serviceProviderChannel = new CoordinatingOAuthChannel(this.SigningElement, false);
consumerChannel.RemoteChannel = serviceProviderChannel;
serviceProviderChannel.RemoteChannel = consumerChannel;
diff --git a/src/DotNetOAuth/ChannelElements/OAuthChannel.cs b/src/DotNetOAuth/ChannelElements/OAuthChannel.cs index 951df05..ee6170c 100644 --- a/src/DotNetOAuth/ChannelElements/OAuthChannel.cs +++ b/src/DotNetOAuth/ChannelElements/OAuthChannel.cs @@ -32,8 +32,13 @@ namespace DotNetOAuth.ChannelElements { /// <param name="signingBindingElement">The binding element to use for signing.</param>
/// <param name="store">The web application store to use for nonces.</param>
/// <param name="tokenManager">The token manager instance to use.</param>
- internal OAuthChannel(ITamperProtectionChannelBindingElement signingBindingElement, INonceStore store, ITokenManager tokenManager)
- : this(signingBindingElement, store, new OAuthMessageTypeProvider(tokenManager), new StandardWebRequestHandler()) {
+ /// <param name="isConsumer">A value indicating whether this channel is being constructed for a Consumer (as opposed to a Service Provider).</param>
+ internal OAuthChannel(ITamperProtectionChannelBindingElement signingBindingElement, INonceStore store, ITokenManager tokenManager, bool isConsumer)
+ : this(
+ signingBindingElement,
+ store,
+ isConsumer ? (IMessageTypeProvider)new OAuthConsumerMessageTypeProvider(tokenManager) : new OAuthServiceProviderMessageTypeProvider(tokenManager),
+ new StandardWebRequestHandler()) {
}
/// <summary>
@@ -43,7 +48,8 @@ namespace DotNetOAuth.ChannelElements { /// <param name="store">The web application store to use for nonces.</param>
/// <param name="messageTypeProvider">
/// An injected message type provider instance.
- /// Except for mock testing, this should always be <see cref="OAuthMessageTypeProvider"/>.
+ /// Except for mock testing, this should always be one of
+ /// <see cref="OAuthConsumerMessageTypeProvider"/> or <see cref="OAuthServiceProviderMessageTypeProvider"/>.
/// </param>
/// <param name="webRequestHandler">
/// An instance to a <see cref="IWebRequestHandler"/> that will be used when submitting HTTP
diff --git a/src/DotNetOAuth/ChannelElements/OAuthConsumerMessageTypeProvider.cs b/src/DotNetOAuth/ChannelElements/OAuthConsumerMessageTypeProvider.cs new file mode 100644 index 0000000..12e46cf --- /dev/null +++ b/src/DotNetOAuth/ChannelElements/OAuthConsumerMessageTypeProvider.cs @@ -0,0 +1,108 @@ +//-----------------------------------------------------------------------
+// <copyright file="OAuthConsumerMessageTypeProvider.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.ChannelElements {
+ using System;
+ using System.Collections.Generic;
+ using DotNetOAuth.Messages;
+ using DotNetOAuth.Messaging;
+
+ /// <summary>
+ /// An OAuth-protocol specific implementation of the <see cref="IMessageTypeProvider"/>
+ /// interface.
+ /// </summary>
+ internal class OAuthConsumerMessageTypeProvider : IMessageTypeProvider {
+ /// <summary>
+ /// The token manager to use for discerning between request and access tokens.
+ /// </summary>
+ private ITokenManager tokenManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OAuthConsumerMessageTypeProvider"/> class.
+ /// </summary>
+ /// <param name="tokenManager">The token manager instance to use.</param>
+ internal OAuthConsumerMessageTypeProvider(ITokenManager tokenManager) {
+ if (tokenManager == null) {
+ throw new ArgumentNullException("tokenManager");
+ }
+
+ this.tokenManager = tokenManager;
+ }
+
+ #region IMessageTypeProvider Members
+
+ /// <summary>
+ /// Analyzes an incoming request message payload to discover what kind of
+ /// message is embedded in it and returns the type, or null if no match is found.
+ /// </summary>
+ /// <param name="fields">The name/value pairs that make up the message payload.</param>
+ /// <remarks>
+ /// The request messages are:
+ /// DirectUserToConsumerMessage
+ /// </remarks>
+ /// <returns>
+ /// The <see cref="IProtocolMessage"/>-derived concrete class that this message can
+ /// deserialize to. Null if the request isn't recognized as a valid protocol message.
+ /// </returns>
+ public Type GetRequestMessageType(IDictionary<string, string> fields) {
+ if (fields == null) {
+ throw new ArgumentNullException("fields");
+ }
+
+ if (fields.ContainsKey("oauth_token")) {
+ return typeof(DirectUserToConsumerMessage);
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// Analyzes an incoming request message payload to discover what kind of
+ /// message is embedded in it and returns the type, or null if no match is found.
+ /// </summary>
+ /// <param name="request">
+ /// The message that was sent as a request that resulted in the response.
+ /// Null on a Consumer site that is receiving an indirect message from the Service Provider.
+ /// </param>
+ /// <param name="fields">The name/value pairs that make up the message payload.</param>
+ /// <returns>
+ /// The <see cref="IProtocolMessage"/>-derived concrete class that this message can
+ /// deserialize to. Null if the request isn't recognized as a valid protocol message.
+ /// </returns>
+ /// <remarks>
+ /// The response messages are:
+ /// UnauthorizedRequestTokenMessage
+ /// GrantAccessTokenMessage
+ /// </remarks>
+ public Type GetResponseMessageType(IProtocolMessage request, IDictionary<string, string> fields) {
+ if (fields == null) {
+ throw new ArgumentNullException("fields");
+ }
+
+ // All response messages have the oauth_token field.
+ if (!fields.ContainsKey("oauth_token")) {
+ return null;
+ }
+
+ // All direct message responses should have the oauth_token_secret field.
+ if (!fields.ContainsKey("oauth_token_secret")) {
+ Logger.Error("An OAuth message was expected to contain an oauth_token_secret but didn't.");
+ return null;
+ }
+
+ if (request is RequestTokenMessage) {
+ return typeof(UnauthorizedRequestTokenMessage);
+ } else if (request is RequestAccessTokenMessage) {
+ return typeof(GrantAccessTokenMessage);
+ } else {
+ Logger.ErrorFormat("Unexpected response message given the request type {0}", request.GetType().Name);
+ throw new ProtocolException(Strings.InvalidIncomingMessage);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOAuth/ChannelElements/OAuthMessageTypeProvider.cs b/src/DotNetOAuth/ChannelElements/OAuthServiceProviderMessageTypeProvider.cs index f1cea03..d47d396 100644 --- a/src/DotNetOAuth/ChannelElements/OAuthMessageTypeProvider.cs +++ b/src/DotNetOAuth/ChannelElements/OAuthServiceProviderMessageTypeProvider.cs @@ -1,5 +1,5 @@ //-----------------------------------------------------------------------
-// <copyright file="OAuthMessageTypeProvider.cs" company="Andrew Arnott">
+// <copyright file="OAuthServiceProviderMessageTypeProvider.cs" company="Andrew Arnott">
// Copyright (c) Andrew Arnott. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
@@ -14,20 +14,17 @@ namespace DotNetOAuth.ChannelElements { /// An OAuth-protocol specific implementation of the <see cref="IMessageTypeProvider"/>
/// interface.
/// </summary>
- /// <remarks>
- /// TODO: split this up into a Consumer and SP provider.
- /// </remarks>
- internal class OAuthMessageTypeProvider : IMessageTypeProvider {
+ internal class OAuthServiceProviderMessageTypeProvider : IMessageTypeProvider {
/// <summary>
/// The token manager to use for discerning between request and access tokens.
/// </summary>
private ITokenManager tokenManager;
/// <summary>
- /// Initializes a new instance of the <see cref="OAuthMessageTypeProvider"/> class.
+ /// Initializes a new instance of the <see cref="OAuthServiceProviderMessageTypeProvider"/> class.
/// </summary>
/// <param name="tokenManager">The token manager instance to use.</param>
- internal OAuthMessageTypeProvider(ITokenManager tokenManager) {
+ internal OAuthServiceProviderMessageTypeProvider(ITokenManager tokenManager) {
if (tokenManager == null) {
throw new ArgumentNullException("tokenManager");
}
@@ -93,38 +90,15 @@ namespace DotNetOAuth.ChannelElements { /// </returns>
/// <remarks>
/// The response messages are:
- /// UnauthorizedRequestTookenMessage
- /// DirectUserToConsumerMessage
- /// GrantAccessTokenMessage
+ /// None.
/// </remarks>
public Type GetResponseMessageType(IProtocolMessage request, IDictionary<string, string> fields) {
if (fields == null) {
throw new ArgumentNullException("fields");
}
- // All response messages have the oauth_token field.
- if (!fields.ContainsKey("oauth_token")) {
- return null;
- }
-
- if (request == null) {
- return typeof(DirectUserToConsumerMessage);
- }
-
- // All direct message responses should haev the oauth_token_secret field.
- if (!fields.ContainsKey("oauth_token_secret")) {
- Logger.Error("An OAuth message was expected to contain an oauth_token_secret but didn't.");
- return null;
- }
-
- if (request is RequestTokenMessage) {
- return typeof(UnauthorizedRequestTokenMessage);
- } else if (request is RequestAccessTokenMessage) {
- return typeof(GrantAccessTokenMessage);
- } else {
- Logger.ErrorFormat("Unexpected response message given the request type {0}", request.GetType().Name);
- throw new ProtocolException(Strings.InvalidIncomingMessage);
- }
+ Logger.Error("Service Providers are not expected to ever receive responses.");
+ return null;
}
#endregion
diff --git a/src/DotNetOAuth/Consumer.cs b/src/DotNetOAuth/Consumer.cs index bc50d1f..3be3f6c 100644 --- a/src/DotNetOAuth/Consumer.cs +++ b/src/DotNetOAuth/Consumer.cs @@ -33,7 +33,7 @@ namespace DotNetOAuth { this.WebRequestHandler = new StandardWebRequestHandler();
ITamperProtectionChannelBindingElement signingElement = serviceDescription.CreateTamperProtectionElement();
INonceStore store = new NonceMemoryStore(StandardExpirationBindingElement.DefaultMaximumMessageAge);
- this.Channel = new OAuthChannel(signingElement, store, new OAuthMessageTypeProvider(tokenManager), this.WebRequestHandler);
+ this.Channel = new OAuthChannel(signingElement, store, new OAuthConsumerMessageTypeProvider(tokenManager), this.WebRequestHandler);
this.ServiceProvider = serviceDescription;
this.TokenManager = tokenManager;
}
@@ -103,21 +103,24 @@ namespace DotNetOAuth { /// <summary>
/// Processes an incoming authorization-granted message from an SP and obtains an access token.
/// </summary>
- /// <returns>The access token.</returns>
+ /// <returns>The access token, or null if no incoming authorization message was recognized.</returns>
public string ProcessUserAuthorization() {
- var authorizationMessage = this.Channel.ReadFromRequest<DirectUserToConsumerMessage>();
-
- // Exchange request token for access token.
- string requestTokenSecret = this.TokenManager.GetTokenSecret(authorizationMessage.RequestToken);
- var requestAccess = new RequestAccessTokenMessage(this.ServiceProvider.AccessTokenEndpoint) {
- RequestToken = authorizationMessage.RequestToken,
- TokenSecret = requestTokenSecret,
- ConsumerKey = this.ConsumerKey,
- ConsumerSecret = this.ConsumerSecret,
- };
- var grantAccess = this.Channel.Request<GrantAccessTokenMessage>(requestAccess);
- this.TokenManager.ExpireRequestTokenAndStoreNewAccessToken(this.ConsumerKey, authorizationMessage.RequestToken, grantAccess.AccessToken, grantAccess.TokenSecret);
- return grantAccess.AccessToken;
+ DirectUserToConsumerMessage authorizationMessage;
+ if (this.Channel.TryReadFromRequest<DirectUserToConsumerMessage>(out authorizationMessage)) {
+ // Exchange request token for access token.
+ string requestTokenSecret = this.TokenManager.GetTokenSecret(authorizationMessage.RequestToken);
+ var requestAccess = new RequestAccessTokenMessage(this.ServiceProvider.AccessTokenEndpoint) {
+ RequestToken = authorizationMessage.RequestToken,
+ TokenSecret = requestTokenSecret,
+ ConsumerKey = this.ConsumerKey,
+ ConsumerSecret = this.ConsumerSecret,
+ };
+ var grantAccess = this.Channel.Request<GrantAccessTokenMessage>(requestAccess);
+ this.TokenManager.ExpireRequestTokenAndStoreNewAccessToken(this.ConsumerKey, authorizationMessage.RequestToken, grantAccess.AccessToken, grantAccess.TokenSecret);
+ return grantAccess.AccessToken;
+ } else {
+ return null;
+ }
}
/// <summary>
diff --git a/src/DotNetOAuth/DotNetOAuth.csproj b/src/DotNetOAuth/DotNetOAuth.csproj index 9eb36d0..0dd6d15 100644 --- a/src/DotNetOAuth/DotNetOAuth.csproj +++ b/src/DotNetOAuth/DotNetOAuth.csproj @@ -57,6 +57,7 @@ <Reference Include="System.XML" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="ChannelElements\OAuthConsumerMessageTypeProvider.cs" />
<Compile Include="ChannelElements\ITokenGenerator.cs" />
<Compile Include="ChannelElements\ITokenManager.cs" />
<Compile Include="ChannelElements\OAuthHttpMethodBindingElement.cs" />
@@ -120,7 +121,7 @@ <Compile Include="Loggers\TraceLogger.cs" />
<Compile Include="Messaging\HttpDeliveryMethod.cs" />
<Compile Include="Messaging\MessageTransport.cs" />
- <Compile Include="ChannelElements\OAuthMessageTypeProvider.cs" />
+ <Compile Include="ChannelElements\OAuthServiceProviderMessageTypeProvider.cs" />
<Compile Include="Messaging\ProtocolException.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Messages\RequestTokenMessage.cs" />
diff --git a/src/DotNetOAuth/Messaging/Channel.cs b/src/DotNetOAuth/Messaging/Channel.cs index e3e49d8..fff8f7c 100644 --- a/src/DotNetOAuth/Messaging/Channel.cs +++ b/src/DotNetOAuth/Messaging/Channel.cs @@ -158,11 +158,61 @@ namespace DotNetOAuth.Messaging { /// Gets the protocol message embedded in the given HTTP request, if present.
/// </summary>
/// <typeparam name="TREQUEST">The expected type of the message to be received.</typeparam>
- /// <returns>The deserialized message, if one is found. Null otherwise.</returns>
+ /// <param name="request">The deserialized message, if one is found. Null otherwise.</param>
+ /// <returns>True if the expected message was recognized and deserialized. False otherwise.</returns>
+ /// <remarks>
+ /// Requires an HttpContext.Current context.
+ /// </remarks>
+ /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current"/> is null.</exception>
+ /// <exception cref="ProtocolException">Thrown when a request message of an unexpected type is received.</exception>
+ internal bool TryReadFromRequest<TREQUEST>(out TREQUEST request)
+ where TREQUEST : class, IProtocolMessage {
+ return TryReadFromRequest<TREQUEST>(this.GetRequestFromContext(), out request);
+ }
+
+ /// <summary>
+ /// Gets the protocol message embedded in the given HTTP request, if present.
+ /// </summary>
+ /// <typeparam name="TREQUEST">The expected type of the message to be received.</typeparam>
+ /// <param name="httpRequest">The request to search for an embedded message.</param>
+ /// <param name="request">The deserialized message, if one is found. Null otherwise.</param>
+ /// <returns>True if the expected message was recognized and deserialized. False otherwise.</returns>
/// <remarks>
/// Requires an HttpContext.Current context.
/// </remarks>
/// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current"/> is null.</exception>
+ /// <exception cref="ProtocolException">Thrown when a request message of an unexpected type is received.</exception>
+ internal bool TryReadFromRequest<TREQUEST>(HttpRequestInfo httpRequest, out TREQUEST request)
+ where TREQUEST : class, IProtocolMessage {
+ IProtocolMessage untypedRequest = this.ReadFromRequest(httpRequest);
+ if (untypedRequest == null) {
+ request = null;
+ return false;
+ }
+
+ request = untypedRequest as TREQUEST;
+ if (request == null) {
+ throw new ProtocolException(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ MessagingStrings.UnexpectedMessageReceived,
+ typeof(TREQUEST),
+ untypedRequest.GetType()));
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// Gets the protocol message embedded in the given HTTP request, if present.
+ /// </summary>
+ /// <typeparam name="TREQUEST">The expected type of the message to be received.</typeparam>
+ /// <returns>The deserialized message.</returns>
+ /// <remarks>
+ /// Requires an HttpContext.Current context.
+ /// </remarks>
+ /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current"/> is null.</exception>
+ /// <exception cref="ProtocolException">Thrown if the expected message was not recognized in the response.</exception>
internal TREQUEST ReadFromRequest<TREQUEST>()
where TREQUEST : class, IProtocolMessage {
return this.ReadFromRequest<TREQUEST>(this.GetRequestFromContext());
@@ -174,32 +224,19 @@ namespace DotNetOAuth.Messaging { /// <typeparam name="TREQUEST">The expected type of the message to be received.</typeparam>
/// <param name="httpRequest">The request to search for an embedded message.</param>
/// <returns>The deserialized message, if one is found. Null otherwise.</returns>
- /// <exception cref="ProtocolException">
- /// Thrown if no message is recognized in the request
- /// or an unexpected type of message is received.
- /// </exception>
+ /// <exception cref="ProtocolException">Thrown if the expected message was not recognized in the response.</exception>
protected internal TREQUEST ReadFromRequest<TREQUEST>(HttpRequestInfo httpRequest)
where TREQUEST : class, IProtocolMessage {
- IProtocolMessage request = this.ReadFromRequest(httpRequest);
- if (request == null) {
+ TREQUEST request;
+ if (this.TryReadFromRequest<TREQUEST>(httpRequest, out request)) {
+ return request;
+ } else {
throw new ProtocolException(
string.Format(
CultureInfo.CurrentCulture,
MessagingStrings.ExpectedMessageNotReceived,
typeof(TREQUEST)));
}
-
- var expectedRequest = request as TREQUEST;
- if (expectedRequest == null) {
- throw new ProtocolException(
- string.Format(
- CultureInfo.CurrentCulture,
- MessagingStrings.UnexpectedMessageReceived,
- typeof(TREQUEST),
- request.GetType()));
- }
-
- return expectedRequest;
}
/// <summary>
diff --git a/src/DotNetOAuth/ServiceProvider.cs b/src/DotNetOAuth/ServiceProvider.cs index 10ea5a7..5d66639 100644 --- a/src/DotNetOAuth/ServiceProvider.cs +++ b/src/DotNetOAuth/ServiceProvider.cs @@ -42,7 +42,7 @@ namespace DotNetOAuth { signingElement.SignatureVerificationCallback = this.TokenSignatureVerificationCallback;
INonceStore store = new NonceMemoryStore(StandardExpirationBindingElement.DefaultMaximumMessageAge);
this.Description = serviceDescription;
- this.Channel = new OAuthChannel(signingElement, store, tokenManager);
+ this.Channel = new OAuthChannel(signingElement, store, tokenManager, false);
this.TokenGenerator = new StandardTokenGenerator();
this.TokenManager = tokenManager;
}
|