I have a fairly standard requirement for an oAuth2 service which allows the client to request the offline_access token and exchange the refresh token for an access token when it has expired.
Although this doesn't seem to be working at all. My client is integrating into Zapier. The zapier client is able to authenticate fine. However, after an hour the token seems to have expired BUT the client hasn't been able to retrieve a new token for whatever reason.
The following shows my persisted grants table showing that (I think) the refresh token has been accessed or created although the expiry time seems to be before the creation time??:
and my client looks like this - details extracted:
Only another thing in the Identityserver4 logs is the following:
I'm not sure why this could be the case?
UPDATE
Zapier definitely reach out and do the refresh:
The refresh of the access token is not something that you can assume is handled automatically for you.
Depending on the client implementation, it might be up to you to do the refresh of the token manually and sometimes it's handled automatically.
It's up to you to adjust the lifetime of your tokens and you can configure them by setting these values:
public int IdentityTokenLifetime { get; set; } = 300; //5 minutes
public int AccessTokenLifetime { get; set; } = 3600; //1 hour
public int AuthorizationCodeLifetime { get; set; } = 300; //5 minutes
public int AbsoluteRefreshTokenLifetime { get; set; } = 2592000; //30 days
public int SlidingRefreshTokenLifetime { get; set; } = 1296000; //15 days
However, there's always a trade-off between convenience and security. Having an access token with long lifetime might be a security issue. Perhaps limit it to one week or two and have the user login again?
Related
in application.properties I need to set the OAuth2 keys...
OAuth2AppClientId=AB............................AN
OAuth2AppClientSecret=br................................u8
OAuth2AppRedirectUri=http://localhost:8085/oauth2redirect
Initially I put the keys in "" quotes assuming they should be treated as a string but to get it working I had to remove them. Can someone explain what's happening with
OAuth2AppClientId=AB............................AN when I build the app
and how do I find out more about OAuth2AppClientId?
A Google search is probably the place to start here. Here's a great resource about what a Client ID and Client Secret are:
https://www.oauth.com/oauth2-servers/client-registration/client-id-secret/
I quote:
The client_id is a public identifier for apps.
The client_secret is a secret known only to the application and the authorization server.
Intuit also has a ton of documentation on OAuth2, and how to implement it. You should read it:
https://developer.intuit.com/app/developer/qbo/docs/develop/authentication-and-authorization/oauth-2.0
In summary, the Client ID is how Intuit identifies that it's your app trying to connect to QuickBooks. Nothing is "happening" to the string when you build/compile the app - it's just normal string. But when your app authenticates against QuickBooks Online, your app sends the Client ID to QuickBooks so that QuickBooks knows it's your app trying to authorize a connection to QuickBooks, and not some other app.
If you want to see how to code is loading this, it is only a property being used inside the application
OAuth2PlatformClientFactory
#Service
#PropertySource(value="classpath:/application.properties", ignoreResourceNotFound=true)
public class OAuth2PlatformClientFactory {
#Autowired
org.springframework.core.env.Environment env;
OAuth2PlatformClient client;
OAuth2Config oauth2Config;
#PostConstruct
public void init() {
// intitialize a single thread executor, this will ensure only one thread processes the queue
oauth2Config = new OAuth2Config.OAuth2ConfigBuilder(env.getProperty("OAuth2AppClientId"), env.getProperty("OAuth2AppClientSecret")) //set client id, secret
.callDiscoveryAPI(Environment.SANDBOX) // call discovery API to populate urls
.buildConfig();
client = new OAuth2PlatformClient(oauth2Config);
}
public OAuth2PlatformClient getOAuth2PlatformClient() {
return client;
}
public OAuth2Config getOAuth2Config() {
return oauth2Config;
}
public String getPropertyValue(String propertyName) {
return env.getProperty(propertyName);
}
}
https://github.com/IntuitDeveloper/OAuth2-JavaWithSDK/blob/master/src/main/java/com/intuit/developer/sampleapp/oauth2/client/OAuth2PlatformClientFactory.java
I'm getting the error We couldn't sign you in. Please try again. when I try to login to my custom web app that uses Azure AD. The client secret expired so I figured I could just create a new one and replace it to see if that fixes it. However my current app doesn't seem to have a client secret.
I used a lot of boilerplate code to set this up originally so I don't know what's going on with this to be honest. Below is my Startup.cs file where I pull the client ID and other stuff from the web.config file. Notice a lack of client secret.
public partial class Startup
{
private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
private static string aadInstance = EnsureTrailingSlash(ConfigurationManager.AppSettings["ida:AADInstance"]);
private static string tenantId = ConfigurationManager.AppSettings["ida:TenantId"];
private static string postLogoutRedirectUri = ConfigurationManager.AppSettings["ida:PostLogoutRedirectUri"];
private static string replyUrl = ConfigurationManager.AppSettings["ida:ReplyUrl"];
private static string authority = aadInstance + tenantId;
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
RedirectUri = replyUrl
});
}
private static string EnsureTrailingSlash(string value)
{
if (value == null)
value = string.Empty;
if (!value.EndsWith("/", StringComparison.Ordinal))
return value + "/";
return value;
}
Can someone point me in the right direction? Or it may be something completely different. After I login it does a bunch of redirects and ends up with the error mentioned above.
Turns out I was accessing the original URL on http and not https and that was causing an issue.
Been looking into the identity server 4 solution to compliment my ASP CORE api.
Using a SPA page on front end, does IdentityServer4 have the capability to manage restfull calls for login/logout/other?
Currently my solution works perfectly to redirect to and from the IdentityServer4 solution, but wondering if i can improve on UX by avoiding the redirects that occur on login/logout?
I've heard of PopUp and iFrame capability, but from research that opens up other risks.
(not sure if this question is for stackoverflow or software engineering stack, happy to move it)
You may do this by using the resource owner password grant type, where you could provide your own login screen and pass the information to IdentityServer.
In IdentityServer you would implement the IResourceOwnerPasswordValidator interface to validate the users.
In your Startup.ConfigureServices add the following.
Services.AddTransient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>();
Here is a sample ResourceOwnerPasswordValidator class.
public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
{
private IUserManager _myUserManager { get; set; }
public ResourceOwnerPasswordValidator(IUserManager userManager)
{
_myUserManager = userManager;
}
public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
{
var user = await _myUserManager.Find(context.UserName, context.Password);
if (user != null)
{
context.Result = new GrantValidationResult(
subject: user.USER_ID,
authenticationMethod: "custom",
claims: await _myUserManager.GetClaimsAsync(user));
}
else
{
context.Result = new GrantValidationResult(
TokenRequestErrors.InvalidRequest,
errorDescription: "UserName or Password Incorrect.");
}
}
}
The IUserManager implements the logic to check the database to validate the user.
Then the SPA client would use the GrantTypes.ResourceOwnerPassword. Here is an example you could start with.
DISCLAIMER
This is not the recommended flow to use.
What is the best way to manage a user session in a Google App Engine application? Ideally I'd like to keep my application stateless and not save any user related data in memory, however I'm also afraid to send user credentials of the network on every request (not to mention authenticating the user on every request would require a call to the Datastore which costs money).
I checked out google's OAuth 2.0 solution but from my understanding it helps if my application wants to connect to any of the google APIs and needs permission from the client to access his google account.
Is there a go to way for managing user session? The most common scenario is to know which user initiated this request without having to send the userId as a request parameter.
Please note that we are not using third party providers. The user registers himself to our page normally and has a custom account. I'm not looking for tools that help integrate authentication with third party services. Otherwise I'd be using google's OAuth 2.0 or similar API
You can Always implement Authenticator Interface.
public class MyAuthenticator implements Authenticator {
#Override
public User authenticate(HttpServletRequest request) {
HttpSession session = request.getSession(false);
//
return null;// if not authenticated, otherwise return User object.
}
}
// Endpoints class.
#Api(name = "example", authenticators = { MyAuthenticator.class })
public class MyEndpoints {
public Profile getProfile(User user) {
if (user == null) {
throw new UnauthorizedException("Authorization required");
}
return new Profile(user.getEmail(), "displayName");
}
// store this class somewhere in models
public class Profile {
private String email;
private String displayName;
public Profile(String email, String displayName) {
this.email = email;
this.displayName = displayName;
}
public String getEmail() {
return email;
}
public String getdisplayName() {
return displayName;
}
}
}
Use the HttpServletRequest object to implement classic session based login or use your own custom header. Well that depends on your case. Return null when not authenticated and return User object when authenticated. Also implement some kind of encryption on both sides(client and server), so as to stop someone having the session key to access your api.
I tried to solve by myself, but... Looks like I need help from people.
I have Business Silverlight application with WCF RIA and EntityFramework. Access to Database I get via LinqToEntites.
Common loading data from database I making by this:
return DbContext.Customers
This code returns full Customers table from DataBase. But sometimes I do not need to show all data. Easy way is use linq filters in client side by next code:
public LoadInfo()
{
...
var LO1 = PublicDomainContext.Load(PublicDomainContext.GetCustomersQuery());
LO1.Completed += LO1Completed;
...
}
private void LO1Completed(object sender, EventArgs eventArgs)
{
...
DatatViewGrid.ItemsSource = null;
DatatViewGrid.ItemsSource = loadOperation.Entities.Where(c=>c ...filtering...);
//or PublicDomainContext.Customers.Where(c=>c ...filtering...)
...
}
However this way has very and very important flaw: all data passing from server to client side via DomainService may be viewed by applications like Fiddler. So I need to come up with another way.
Task: filter recieving data in server side and return this data.
Way #1: LinqToEntites has a beautiful projection method:
//MSDN Example
var query =
contacts.SelectMany(
contact => orders.Where(order =>
(contact.ContactID == order.Contact.ContactID)
&& order.TotalDue < totalDue)
.Select(order => new
{
ContactID = contact.ContactID,
LastName = contact.LastName,
FirstName = contact.FirstName,
OrderID = order.SalesOrderID,
Total = order.TotalDue
}));
But, unfortunately, DomainServices cannot return undefined types, so this way won't work.
Way #2: I found next solution - make separate DTO classes (DataTransferObject). I just read some samples and made on the server side next class:
[DataContract]
public partial class CustomerDTO
{
[DataMember]
public int ISN { get; set; }
[DataMember]
public string FIO { get; set; }
[DataMember]
public string Listeners { get; set; }
}
And based this class I made a row of methods which return filtered data:
[OperationContract]
public List<CustomerDTO> Customers_Common()
{
return DbContext.Customers....Select(c => new CustomerDTO { ISN = c.ISN, FIO = c.FIO, Listeners = c.Listeners }).ToList();
}
And this works fine, all good...
But, there is strange problem: running application locally does not affect any troubles, but after publishing project on the Web Site, DomainService returns per each method HTTP 500 Error ("Not Found" exception). Of course, I cannot even LogIn into my application. DomainService is dead. If I delete last class and new methods from application and republish - all works fine, but without speacial filtering...
The Question: what I do wrong, why Service is dying with new classes, or tell me another way to solve my trouble. Please.
U P D A T E :
Hey, finally I solved this!
There is an answer: Dynamic query with WCF RIA Services
Your best shot is to find out what is causing the error. For that, override the OnError method on the DomainService like this:
protected override void OnError(DomainServiceErrorInfo errorInfo)
{
/* Log the error info to a file. Don't forget inner exceptions.
*/
base.OnError(errorInfo);
}
This is useful, because only two exceptions will be passed to the client, so if there are a lot of nested inner exceptions, you should still be able to see what actually causes the error.
In addition, you can inspect the error by attaching the debugger to the browser instance you are opening the site with. In VS2010 this is done by doing [Debug] -> [Attach to Process] in the menu-bar.