How can I use Kerberos / NTLM authentication (like in the HttpClient) within the OData Client from Microsoft (Microsoft.OData.Client)?
I am using the package Microsoft.OData.Client 7.9.0 and I am trying to connect to a OData endpoint with https and authentication enabled. However I am not able to retrieve any data, instead this exception is thrown:
Microsoft.OData.Edm.Csdl.EdmParseException: "Encountered the following errors when parsing the CSDL document:
XmlError : Root element is missing. : (0, 0)"
It seems that the context could not find the requested resource because of a lack of permissions. This is the referencial implementation:
// Simple data class
public class Person
{
public string Id { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
// OData service context
public class Container : DataServiceContext
{
public DataServiceQuery<Person> People { get; }
public Container(Uri serviceRoot) : base(serviceRoot)
{
People = base.CreateQuery<Person>(nameof(People));
// This is not working ...
Credentials = new NetworkCredential("user", #"p#ssw0rd!");
}
}
Container context = new Container(new Uri("https://targetservice.dev/ODataV4/$metadata"));
var result = context.People.Execute() as QueryOperationResponse<Person>;
Providing credentials to the Credentials property does not seem to have any effect here.
Set the credentials from the outside, before using the container for the first time. After all, you don't want to modify your Container every time the credentials change. Nor do you want to hard-code into the Container the mechanism you use to retrieve those credentials.
Assuming both the server and client are in the same domain, one can connect using Windows authentication as the current user, with CredentialCache.DefaultNetworkCredentials
var uri=new Uri("https://targetservice.dev/ODataV4/$metadata");
Container context = new Container(uri){
Credentials = CredentialCache.DefaultNetworkCredentials
};
var result = context.People.Execute() as QueryOperationResponse<Person>;
If you want to connect from a non-Domain machine or use a different account, you'll have to create a NetworkCredential instance :
var credential = new NetworkCredential("MyDomain","UserName","Password");
Container context = new Container(uri){
Credentials = credential
};
The main advantage of Windows authentication is that you don't need to explicitly specify the credentials. Any remote calls will be made using the current account. This way there's no password to store, change or leak. This of course assumes that both the client and server are in the same Windows domain, otherwise the client account won't be recognized by the server.
Wrong URL
The exception snippet complains about the server's response, not failed authentication. XmlError : Root element is missing. : (0, 0) means that the response was not an XML document. It's not an error response either. If the server had responded with a 4xx or 5xx status, a different exception would be thrown.
The service URL is wrong and shouldn't contain the $metadata suffix. As the constructor name says DataServiceContext(Uri serviceRoot) the URL should be the service's root. That URL is stored in the BaseUriResolver property:
internal DataServiceContext(Uri serviceRoot, ODataProtocolVersion maxProtocolVersion, ClientEdmModel model)
{
Debug.Assert(model != null, "model != null");
this.model = model;
this.baseUriResolver = UriResolver.CreateFromBaseUri(serviceRoot, ServiceRootParameterName);
The metadata URL is created by the GetMetadataUri method whose code is:
public virtual Uri GetMetadataUri()
{
// TODO: resolve the location of the metadata endpoint for the service by using an HTTP OPTIONS request
Uri metadataUri = UriUtil.CreateUri(UriUtil.UriToString(
this.BaseUriResolver.GetBaseUriWithSlash()) +
XmlConstants.UriMetadataSegment,
UriKind.Absolute);
return metadataUri;
}
Using https://targetservice.dev/ODataV4/$metadata as the service URL would result in an invalid https://targetservice.dev/ODataV4/$metadata/$metadata metadata URL
To troubleshoot such errors, Fiddler or another debugging proxy can be used to intercept HTTP calls and inspect what gets sent and what is actually returned by the server.
After a while of research and testing, I managed to get this working. Indeed the requested resource (CSDL) could not be requested because of a lack of permissions. But the CSDL can be requested using a HttpClient initially, which takes the supplied credentials into account:
public class Container : DataServiceContext
{
public override ICredentials Credentials { get => base.Credentials; set => throw new NotSupportedException(); }
public bool UseDefaultCredentials { get; }
public DataServiceQuery<Person> People { get; }
private static IEdmModel? ParsedModel;
public Container(Uri serviceRoot, ICredentials? credentials = null, bool useDefaultCredentials = false) : base(serviceRoot)
{
if (serviceRoot is null) throw new ArgumentNullException(nameof(serviceRoot));
base.Credentials = useDefaultCredentials ? credentials ?? CredentialCache.DefaultCredentials : credentials;
UseDefaultCredentials = useDefaultCredentials;
People = base.CreateQuery<Person>(nameof(People));
// It is required to load the service model initially.
Format.LoadServiceModel = () => RequestModel();
Format.UseJson();
}
// This method requets the service model directly from the OData endpoint via HttpClient.
// It also uses the supplied credentials.
private IEdmModel RequestModel()
{
if (ParsedModel is not null) return ParsedModel;
// Create http client (+ handler) populated with credentials.
using HttpClientHandler clientHandler = new()
{
Credentials = UseDefaultCredentials ? CredentialCache.DefaultCredentials : Credentials,
CheckCertificateRevocationList = true
};
using HttpClient httpClient = new(clientHandler)
{
BaseAddress = new UriBuilder()
{
Scheme = BaseUri.Scheme,
Host = BaseUri.Host,
Port = BaseUri.Port
}.Uri
};
// Process the response stream via XmlReader and CsdlReader.
using var responseStream = httpClient.GetStreamAsync(
new Uri(Path.Combine(BaseUri.AbsolutePath, "$metadata"), UriKind.Relative))
.ConfigureAwait(false).GetAwaiter().GetResult();
using var xmlReader = XmlReader.Create(responseStream);
if (!CsdlReader.TryParse(xmlReader, out var model, out var errors))
{
StringBuilder errorMessages = new();
foreach (var error in errors) errorMessages.Append(error.ErrorMessage).Append("; ");
throw new InvalidOperationException(errorMessages.ToString());
}
// Return and cache the parsed model.
return ParsedModel = model;
}
}
By then it can be used the following:
Container context = new Container(new Uri("https://targetservice.dev/ODataV4/"), useDefaultCredentials: true);
UPDATE
This seems to be a bug in the implementation not forwarding credential information to the metadata request. The following stacktrace shows the call of the LoadServiceModelFromNetwork method:
at Microsoft.OData.Edm.Csdl.CsdlReader.Parse(XmlReader reader)
at Microsoft.OData.Client.DataServiceClientFormat.LoadServiceModelFromNetwork()
at Microsoft.OData.Client.DataServiceClientFormat.get_ServiceModel()
at Microsoft.OData.Client.RequestInfo..ctor(DataServiceContext context)
at Microsoft.OData.Client.DataServiceRequest.CreateExecuteResult(Object source, DataServiceContext context, AsyncCallback callback, Object state, String method)
at Microsoft.OData.Client.DataServiceRequest.Execute[TElement](DataServiceContext context, QueryComponents queryComponents)
at Microsoft.OData.Client.DataServiceQuery`1.Execute()
The current implementation within the package is the following:
internal IEdmModel LoadServiceModelFromNetwork()
{
DataServiceClientRequestMessage httpRequest;
BuildingRequestEventArgs requestEventArgs = null;
// test hook for injecting a network request to use instead of the default
if (InjectMetadataHttpNetworkRequest != null)
// ...
else
{
// ...
httpRequest = new HttpClientRequestMessage(args);
}
// ...
Task<IODataResponseMessage> asyncResponse =
Task<IODataResponseMessage>.Factory.FromAsync(httpRequest.BeginGetResponse, httpRequest.EndGetResponse,
httpRequest);
IODataResponseMessage response = asyncResponse.GetAwaiter().GetResult();
// ...
using (StreamReader streamReader = new StreamReader(response.GetStream()))
using (XmlReader xmlReader = XmlReader.Create(streamReader))
{
return CsdlReader.Parse(xmlReader);
}
}
As it turns out the httpRequest variable here is responsible for handling the actual response. The constructor is implemented as follows:
public HttpClientRequestMessage(DataServiceClientRequestMessageArgs args)
: base(args.ActualMethod)
{
_messageStream = new MemoryStream();
_handler = new HttpClientHandler();
_client = new HttpClient(_handler, disposeHandler: true);
_contentHeaderValueCache = new Dictionary<string, string>();
_effectiveHttpMethod = args.Method;
_requestUrl = args.RequestUri;
_requestMessage = new HttpRequestMessage(new HttpMethod(this.ActualMethod), _requestUrl);
// Now set the headers.
foreach (KeyValuePair<string, string> keyValue in args.Headers)
{
this.SetHeader(keyValue.Key, keyValue.Value);
}
}
Neither the credentials nor the boolean UseDefaultCredentials is forwarded to the HttpClientHandler. But the args provide this information.
Also the credentials are not set after construction and the response is not checked for an invalid status code so that it ends up in this strange behavior.
Related
Purpose:
I work with a third party that has implemented an OAuth2 authorization code flow. The goal is for an authenticated user in my app to be able to access areas of a this third parties app without having to sign in to the third party. I have provided the third party with our authorization url, a clientId and a callback url (not sure how this is used).
I am trying to wire up a basic authorization code flow using OAuth2, but I am stuck on an invalid_grant error and am having trouble determining how the process is intended to work.
Here is what I have implemented thus far:
In the startup class I configure my OAuth server to include an AuthorizeEndpointPath and an AuthorizationCodeProvider:
var allowInsecureHttp = bool.Parse(ConfigurationManager.AppSettings["AllowInsecureHttp"]);
var oAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = allowInsecureHttp,
TokenEndpointPath = new PathString("/oauth2/token"),
AuthorizeEndpointPath = new PathString("/oauth2/authorize"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
Provider = new CustomOAuthProvider(HlGlobals.Kernel),
AccessTokenFormat = new CustomJwtFormat(_baseUrl, HlGlobals.Kernel),
AuthorizationCodeProvider = new SimpleAuthenticationTokenProvider()
};
The CustomOAuthProider contains the following overrides:
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
//We validated that Client Id and redirect Uri are what we expect
if (context.ClientId == "123456" && context.RedirectUri.Contains("localhost"))
{
context.Validated();
}
else
{
context.Rejected();
}
return Task.FromResult<object>(null);
}
public override Task AuthorizeEndpoint(OAuthAuthorizeEndpointContext context)
{
var ci = new ClaimsIdentity("Bearer");
context.OwinContext.Authentication.SignIn(ci);
context.RequestCompleted();
return Task.FromResult<object>(null);
}
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string clientId;
string clientSecret;
Guid clientIdGuid;
// Validate the context
context.Validated();
return Task.FromResult<object>(0);
}
Finally, the code in the SimpleAuthenticationTokenProvider is implemented as follows:
public SimpleAuthenticationTokenProvider() : base()
{
this.OnCreate = CreateCode;
this.OnReceive = ReceiveCode;
}
private readonly ConcurrentDictionary<string, string> _authenticationCodes = new ConcurrentDictionary<string, string>(StringComparer.Ordinal);
public void CreateCode(AuthenticationTokenCreateContext context)
{
context.SetToken(Guid.NewGuid().ToString("n") + Guid.NewGuid().ToString("n"));
_authenticationCodes[context.Token] = context.SerializeTicket();
}
public void ReceiveCode(AuthenticationTokenReceiveContext context)
{
string value;
_authenticationCodes.TryGetValue(context.Token, out value);
context.DeserializeTicket(value);
}
I have wired up a sample request inside of Postman. When I execute the postman oauth2 Authroization Code grant type the following methods from above fire sequentially:
ValidateClientRedirectUri
AuthorizeEndpoint
ValidateClientAuthentication
ReceiveCode
I am getting back an 'invalid_grant' error from postman, and no authorization code has been returned.
Can anyone point out where I may be going wrong here?
Secondly, how is the callbackurl/redirect url supposed to be used? Is it a fallback to the login page if the user has not authenticated?
UPDATE
I see that a 'code' is coming back in the querystring attached to the redirectURL on the response. Should the redirectURL be a page on the third-party site?
How is this code exchanged for a token?
UPDATE 2
Another question: At what phase do I read/parse the code? I can see the code coming in on the request querystring in my GrantClientCredentials method. Should I just parse it from the querystring and validate? At what point should ReceiveCode be called?
I have the following scenario going on:
A windows "fat client" application is connecting to a WCF webservice. Both, client and webservice use exact the same binding, which looks like this:
private static NetTcpBinding Message_Security_UserName_Credentials()
{
NetTcpBinding binding = new NetTcpBinding();
binding.Security.Mode = SecurityMode.Message;
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
binding.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign;
binding.PortSharingEnabled = true;
return binding;
}
The client sends "custom" client credentials to the webservice. The custom client credential class is this:
public class CustomClientCredentials : ClientCredentials
{
public CustomClientCredentials()
{
AuthorizationToken = String.Empty;
this.ClientCertificate.Certificate = Certificates.ClientPFX;
this.ServiceCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.Custom;
this.ServiceCertificate.Authentication.CustomCertificateValidator = new CustomClientX509CertificateValidator("CN");
}
private string authorizationtoken;
public string AuthorizationToken
{
get
{
return this.authorizationtoken;
}
set
{
if (value == null)
{
throw new ArgumentNullException("value");
}
this.authorizationtoken = value;
}
}
public String Name
{
set
{
this.UserName.UserName = value;
}
}
public String Password
{
set
{
this.UserName.Password = value;
}
}
protected CustomClientCredentials(CustomClientCredentials other)
: base(other)
{
this.AuthorizationToken = other.AuthorizationToken;
}
protected override ClientCredentials CloneCore()
{
return new CustomClientCredentials(this);
}
}
In short, the process of sending the custom client credentials to the service looks like this:
ChannelFactory<ILoginService> factory = new ChannelFactory<ILoginService> (binding, endpointaddress);
factory.Endpoint.Behaviors.Remove<ClientCredentials>();
CustomClientCredentials credentials = new CustomClientCredentials() {Name = this.User.EMail, Password = this.User.Password, AuthorizationToken = String.Empty};
factory.Endpoint.Behaviors.Add(credentials);
ILoginService client = factory.CreateChannel();
Token result = client.LogIn();
On the server, I use a custom UserPasswordValidator to read out the client credentials. It looks like this:
public class CustomServiceUserNamePasswordValidator : System.IdentityModel.Selectors.UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
if (null == userName || null == password)
{
throw new ArgumentNullException();
}
}
}
Up to this point everything works fine. As you can see in my custom ClientCredentials class, I want to send more additional information to the server.
My question is: What must I do, to read out the received custom client credentials on the server?
The theory in my head is, that I simply must tell the service endpoint on the server, that he should expect a certain type of credentials and then he can evaluate them.
Validating custom client credentials may not an easy tasks but you can following this link for validation. I would suggest also to follow this link for custom credential implementation.
I have successfully added OAuth to my WebAPI 2 project using OWIN. I receive tokens and can use them in the HTTP Header to access resources.
Now I want to use those tokens also on other channels for authentication that are not the standard HTTP requests that the OWIN template is made for. For example, I am using WebSockets where the client has to send the OAuth Bearer Token to authenticate.
On the server side, I receive the token through the WebSocket. But how can I now put this token into the OWIN pipeline to extract the IPrincipal and ClientIdentifier from it? In the WebApi 2 template, all this is abstracted for me, so there is nothing I have to do to make it work.
So, basically, I have the token as a string and want to use OWIN to access the user information encoded in that token.
Thank you in advance for the help.
I found a part of the solution in this blog post: http://leastprivilege.com/2013/10/31/retrieving-bearer-tokens-from-alternative-locations-in-katanaowin/
So I created my own Provider as follows:
public class QueryStringOAuthBearerProvider : OAuthBearerAuthenticationProvider
{
public override Task RequestToken(OAuthRequestTokenContext context)
{
var value = context.Request.Query.Get("access_token");
if (!string.IsNullOrEmpty(value))
{
context.Token = value;
}
return Task.FromResult<object>(null);
}
}
Then I needed to add it to my App in Startup.Auth.cs like this:
OAuthBearerOptions = new OAuthBearerAuthenticationOptions()
{
Provider = new QueryStringOAuthBearerProvider(),
AccessTokenProvider = new AuthenticationTokenProvider()
{
OnCreate = create,
OnReceive = receive
},
};
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
With a custom AuthenticationTokenProvider, I can retrieve all other values from the token early in the pipeline:
public static Action<AuthenticationTokenCreateContext> create = new Action<AuthenticationTokenCreateContext>(c =>
{
c.SetToken(c.SerializeTicket());
});
public static Action<AuthenticationTokenReceiveContext> receive = new Action<AuthenticationTokenReceiveContext>(c =>
{
c.DeserializeTicket(c.Token);
c.OwinContext.Environment["Properties"] = c.Ticket.Properties;
});
And now, for example in my WebSocket Hander, I can retrieve ClientId and others like this:
IOwinContext owinContext = context.GetOwinContext();
if (owinContext.Environment.ContainsKey("Properties"))
{
AuthenticationProperties properties = owinContext.Environment["Properties"] as AuthenticationProperties;
string clientId = properties.Dictionary["clientId"];
...
}
By default, OWIN use ASP.NET machine key data protection to protect the OAuth access token when hosted on IIS. You can use MachineKey class in System.Web.dll to unprotect the tokens.
public class MachineKeyProtector : IDataProtector
{
private readonly string[] _purpose =
{
typeof(OAuthAuthorizationServerMiddleware).Namespace,
"Access_Token",
"v1"
};
public byte[] Protect(byte[] userData)
{
throw new NotImplementedException();
}
public byte[] Unprotect(byte[] protectedData)
{
return System.Web.Security.MachineKey.Unprotect(protectedData, _purpose);
}
}
Then, construct a TicketDataFormat to get the AuthenticationTicket object where you can get the ClaimsIdentity and AuthenticationProperties.
var access_token="your token here";
var secureDataFormat = new TicketDataFormat(new MachineKeyProtector());
AuthenticationTicket ticket = secureDataFormat.Unprotect(access_token);
To unprotect other OAuth tokens, you just need to change the _purpose content. For detailed information, see OAuthAuthorizationServerMiddleware class here:
http://katanaproject.codeplex.com/SourceControl/latest#src/Microsoft.Owin.Security.OAuth/OAuthAuthorizationServerMiddleware.cs
if (Options.AuthorizationCodeFormat == null)
{
IDataProtector dataProtecter = app.CreateDataProtector(
typeof(OAuthAuthorizationServerMiddleware).FullName,
"Authentication_Code", "v1");
Options.AuthorizationCodeFormat = new TicketDataFormat(dataProtecter);
}
if (Options.AccessTokenFormat == null)
{
IDataProtector dataProtecter = app.CreateDataProtector(
typeof(OAuthAuthorizationServerMiddleware).Namespace,
"Access_Token", "v1");
Options.AccessTokenFormat = new TicketDataFormat(dataProtecter);
}
if (Options.RefreshTokenFormat == null)
{
IDataProtector dataProtecter = app.CreateDataProtector(
typeof(OAuthAuthorizationServerMiddleware).Namespace,
"Refresh_Token", "v1");
Options.RefreshTokenFormat = new TicketDataFormat(dataProtecter);
}
in addition to johnny-qian answer, using this method is better to create DataProtector. johnny-qian answer, depends on IIS and fails on self-hosted scenarios.
using Microsoft.Owin.Security.DataProtection;
var dataProtector = app.CreateDataProtector(new string[] {
typeof(OAuthAuthorizationServerMiddleware).Namespace,
"Access_Token",
"v1"
});
What is your token like, is it an encrypt string or a formatted string, what is it format?
I my code:
public static Action<AuthenticationTokenReceiveContext> receive = new Action<AuthenticationTokenReceiveContext>(c =>
{
if (!string.IsNullOrEmpty(c.Token))
{
c.DeserializeTicket(c.Token);
//c.OwinContext.Environment["Properties"] = c.Ticket.Properties;
}
});
The c.Ticket is always null.
I'm trying to create a simple web service with ServiceStack. On the server (service) side, I've first created a user "administrator" and assigned it the "Admin" role. I'm using the ServiceStack built-in credentials authentication and I it authenticates nicely. However, any subsequent call to any webservice that is decorated with the [Authenticate] attribute returns a the following error:
No configuration was added for OAuth provider 'basic'
FYI: the database is RavenDb - using ServiceStack.Authentication.RavenDb. I also registered a MemoryCacheClient in the App_Start.AppHost.Configure(). Let me know if you need to see all code in App_Start.AppHost class.
Server Code (excerpt):
namespace MyTest
{
[Route("/hello")]
[Route("/hello/{Name}")]
[Authenticate]
public class Hello
{
public string Name { get; set; }
}
public class HelloResponse
{
public string Result { get; set; }
}
public class HelloService : Service
{
public IUserAuthRepository UserAuthRepo { get; set; }
public object Any(Hello request)
{
return new HelloResponse { Result = "Hello, " + request.Name };
}
}
}
Client side:
var client = new JsonServiceClient("http://127.0.0.1:65385/auth/credentials?format=json");
var auth = new Auth { UserName = "administrator", Password = "12345" };
var authResponse = client.Post(auth);
if (authResponse.ResponseStatus.ErrorCode == null)
{
client = new JsonServiceClient("http://127.0.0.1:65385/hello");
client.UserName = "administrator";
client.Password = "12345";
// When sending the request - it returns "Not Found"
var helloResponse = client.Send(new Hello { Name = "John" });
}
EDIT:
The web.config of my services looks exactly like written in section 2a of the Hello World tutorial
Here's the configuration of my AppHost:
public override void Configure(Funq.Container container)
{
container.Register(new MemoryCacheClient { FlushOnDispose = false });
container.Register(new EmbeddableDocumentStore { DataDirectory = "Data" }.Initialize());
ConfigureAuth(container);
}
private void ConfigureAuth(Funq.Container container)
{
var appSettings = new AppSettings();
Plugins.Add(new AuthFeature(() => new CustomUserSession(),
new IAuthProvider[] {
new CredentialsAuthProvider(appSettings)}));
Plugins.Add(new RegistrationFeature());
container.Register(
new RavenUserAuthRepository(c.Resolve()));
}
Firstly you should look at ServiceStack's AuthTests for some examples that test authentication with ServiceStack's C# Service Clients.
You're passing the wrong urls in your C# ServiceStack client, i.e. you should only pass in the Base URI where ServiceStack is hosted, e.g:
var client = new JsonServiceClient(
"http://127.0.0.1:65385/auth/credentials?format=json");
var client = new JsonServiceClient("http://127.0.0.1:65385/hello");
Should instead only be:
var client = new JsonServiceClient("http://127.0.0.1:65385/");
var client = new JsonServiceClient("http://127.0.0.1:65385/");
You should also never put ?format=json when using a ServiceStack C# ServiceClient like JsonServiceClient, since the format is already implied in the client which ensures JSON is returned by sending the Accept: application/json HTTP Header upon every request.
OK, found out what the problem was: The [Authenticate], [RequiredRole] and [RequiredPermission] attributes work with Basic Authentication (Authenticate works with Digest Authentication as well). So if you want to use any of those attributes, you must make sure that you have added the BasicAuthProvider to the list of IAuthProviders when setting up the AuthFeature as a plugin. So,
Plugins.Add(new AuthFeature(() => new CustomUserSession(),
new IAuthProvider[] {
new CredentialsAuthProvider(appSettings)}));
must be
Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[] {
new CredentialsAuthProvider(appSettings),
new BasicAuthProvider()
}));
I still have to send username and password in clear text with every call though ...
Make sure you register an ICacheClient in your ServiceStack application and then you shouldn't have to send the user name and password with each request. Your client side should be able to handle storing a cookie without any effort on your end.
I have the standard Hello World web service, with my custom user auth session, because I wanted some additional parameters. The authentication part works as expected. Below is my CustomUserSession:
public class CustomUserSession : AuthUserSession
{
public int? PortalId { get; set; }
public override void OnAuthenticated(ServiceInterface.IServiceBase authService, IAuthSession session, IOAuthTokens tokens, Dictionary<string, string> authInfo)
{
base.OnAuthenticated(authService, session, tokens, authInfo);
var refId = session.UserAuthId;
var userRep = new InMemoryAuthRepository();
var userAuth = userRep.GetUserAuth(refId.ToString());
PortalId = userAuth.RefId;
}
}
I have the refId holding a custom parameter from one of my other tables, and it gets the right value when I debug. My question is, can I now call methods of the webservice from within this method? So, for example, if I had an execute method that accepted an int, can I call it from within the overridden OnAuthenticated method?
You can execute a Service in ServiceStack using ResolveService<T>.
From inside a custom user session:
using (var service = authService.ResolveService<MyService>())
{
var response = service.Get(new MyRequest { ... });
}
From inside a ServiceStack Service:
using (var service = base.ResolveService<MyService>())
{
var response = service.Get(new MyRequest { ... });
}
From outside of ServiceStack:
using (var service = HostContext.ResolveService<MyService>())
{
var response = service.Get(new MyRequest { ... });
}
Try resolving ServiceInterface.IServiceBase to proper class/interface.
Here's an example for IDbConnectionFactory:
//Resolve the DbFactory from the IOC and persist the info
authService.TryResolve<IDbConnectionFactory>().Exec(dbCmd => dbCmd.Save(PortalId));