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.
Related
I would like to use .net variant of Google Cloud Client Libraries (Resource Manager for creating new project, for example).
I wouldn't like to use neither service account credentials nor ADC.
Can I somehow pass existing OAuth credentials (access token, obtained for appropriate scope) to the client library to authenticate the given user?
(Or) do I need any authentication client library?
Briefly looked at the ProjectsClientBuilder class, but seems heavy generated (also as the documentation), meaning it's a bit harder to find any hint.
The following example shows how to authorize the Google cloud resource manager API using Oauth2 for an installed app.
// Key file from google developer console (INSTALLED APP)
var PathToInstalledKeyFile = #"C:\Development\FreeLance\GoogleSamples\Credentials\credentials.json";
// scope of authorization needed for the method in question.
var scopes = "https://www.googleapis.com/auth/cloud-platform";
// Installed app authorizaton.
var credential = GoogleWebAuthorizationBroker.AuthorizeAsync(GoogleClientSecrets.FromFile(PathToInstalledKeyFile).Secrets,
new []{ scopes },
"userName",
CancellationToken.None,
new FileDataStore("usercreds", true)).Result;
var client = new ProjectsClientBuilder()
{
Credential = credential,
}.Build();
var projects = client.ListProjects(new FolderName("123"));
Note for a web application the code will be different. Web authorization is not the same with the client library. I havent tried to connect any of the cloud apis via web oauth before.
As mentioned above, only thing needed is to initialize Credential property in the project builder prior the Build().
Just for the completeness:
// when using Google.Apis.CloudResourceManager.v3
public class Program
{
private static async Task OlderMethod(string oAuthToken)
{
using var service = new CloudResourceManagerService();
var id = Guid.NewGuid().ToString("N")[..8];
var project = new Google.Apis.CloudResourceManager.v3.Data.Project
{
DisplayName = $"Prog Created {id}",
ProjectId = $"prog-created-{id}",
};
var createRequest = service.Projects.Create(project);
createRequest.Credential = new OlderCredential(oAuthToken);
var operation = await createRequest.ExecuteAsync();
// ...
}
}
public class OlderCredential : IHttpExecuteInterceptor
{
private readonly string oAuthToken;
public OlderCredential(string oAuthToken) { this.oAuthToken = oAuthToken; }
public Task InterceptAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", oAuthToken);
return Task.CompletedTask;
}
}
In the end the newer one is simpler, just returning the token, no need to modify the request itself:
// when using Google.Cloud.ResourceManager.V3
public class Program
{
private static async Task NewerMethod(string oAuthToken)
{
var client = await new ProjectsClientBuilder
{
Credential = new NewerCredential(oAuthToken),
}.BuildAsync();
var id = Guid.NewGuid().ToString("N")[..8];
var project = new Project
{
DisplayName = $"Prog Created New {id}",
ProjectId = $"prog-created-new-{id}",
};
var operation = await client.CreateProjectAsync(project);
}
}
public class NewerCredential : ICredential
{
private readonly string oAuthToken;
public NewerCredential(string oAuthToken) { this.oAuthToken = oAuthToken; }
public void Initialize(ConfigurableHttpClient httpClient) { }
public Task<string> GetAccessTokenForRequestAsync(string? authUri, CancellationToken cancellationToken) => Task.FromResult(oAuthToken);
}
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.
I have a requirement like below to implement REST API using OAuth 2.0 and Web Api.
REST API should allow
- to create, update, view and delete orders
- to create, update, view and delete inventories
API should be able to used by any type of external client such as web application, mobile application, windows/web services, etc.
Roles allowed for external clients : Order Management , Inventory Management
User data (roles, permissions) of external clients will not be managed by our system.
Note: There can be another two roles like Internal , External. Because delete functions can't be allowed for external users.
Order and Inventory data will be managed in a SQL Server DB which is already used by current windows/desktop applications. Orders, inventories comes via new API should save in same database.
Questions:
Which grant type I can use?
How should I mange external client's data (allowed roles, client id, tokens) ? Do I need to use separate membership database for this? Can I used my existing database with new tables for this?
You can use Microsoft.Owin.Security.OAuth provider. Please have a look on following sample.
Create new Owin Startup file and change the Configuration method as following
public void Configuration(IAppBuilder app)
{
var oauthProvider = new OAuthAuthorizationServerProvider
{
OnGrantClientCredentials = async context =>
{
var claimsIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
// based on clientId get roles and add claims
claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, "Developer"));
claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, "Developer2"));
context.Validated(claimsIdentity);
},
OnValidateClientAuthentication = async context =>
{
string clientId;
string clientSecret;
// use context.TryGetBasicCredentials in case of passing values in header
if (context.TryGetFormCredentials(out clientId, out clientSecret))
{
if (clientId == "clientId" && clientSecret == "secretKey")
{
context.Validated(clientId);
}
}
}
};
var oauthOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/accesstoken"),
Provider = oauthProvider,
AuthorizationCodeExpireTimeSpan = TimeSpan.FromMinutes(1),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(3),
SystemClock = new SystemClock()
};
app.UseOAuthAuthorizationServer(oauthOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
var config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
app.UseWebApi(config);
}
And authorize your API like this
[Authorize(Roles = "Developer")]
// GET: api/Tests
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
you can consume it like following,
string baseAddress = "http://localhost/";
var client = new HttpClient();
// you can pass the values in Authorization header or as form data
//var authorizationHeader = Convert.ToBase64String(Encoding.UTF8.GetBytes("clientId:secretKey"));
//client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authorizationHeader);
var form = new Dictionary<string, string>
{
{"grant_type", "client_credentials"},
{"client_id", "clientId"},
{"client_secret", "secretKey"},
};
var tokenResponse = client.PostAsync(baseAddress + "accesstoken", new FormUrlEncodedContent(form)).Result;
var token = tokenResponse.Content.ReadAsAsync<Token>(new[] { new JsonMediaTypeFormatter() }).Result;
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.AccessToken);
var authorizedResponse = client.GetAsync(baseAddress + "/api/Tests").Result;
Token.cs
internal class Token
{
[JsonProperty("access_token")]
public string AccessToken { get; set; }
[JsonProperty("token_type")]
public string TokenType { get; set; }
[JsonProperty("expires_in")]
public int ExpiresIn { get; set; }
[JsonProperty("refresh_token")]
public string RefreshToken { get; set; }
}
answers to your questions
You can use client_credentials
Maintain roles in your own database for each client and inside OnGrantClientCredentials just get roles by client id and assign as claims.
Here is a starting point which Grant to choose for which Client. In addition, if you build a SPA (even it is a first party client according to the wording from the link), I would prefer Implicit Grant. If you have a question about a particular Grant for a particular Client, create a new question on stackoverflow.
You can use IdentityServer3 with IdentityServer3.EntityFramework and IdentityServer3.AspNetIdentity. You can place IdentityServer tables in an existing database but I would not recommend it for production.
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 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));