tag:blogger.com,1999:blog-52196651790846020822024-02-20T02:40:00.877-08:00David CodesThoughts on Appengine, Java, AngularJS, web applications and IT in generalAnonymoushttp://www.blogger.com/profile/02803423273909086751noreply@blogger.comBlogger17125tag:blogger.com,1999:blog-5219665179084602082.post-24145392725309608082015-01-25T02:13:00.000-08:002015-05-15T10:45:09.014-07:00A bit of fun with the Google Maps API<script>
location.href="https://david-codes.hatanian.com/2015/01/25/a-bit-of-fun-with-google-maps-api.html";
</script>
<div style="text-align: justify;">
A lot of Google Maps use cases require to plot regions, such as sales sectors, maintenance zones or potential
customer markets.</div>
<br />
<div style="text-align: justify;">
Doing this manually is often tedious and error-prone. Since a lot of use cases are based on
administrative boundaries (states, cities and others), our best option
is to use the administrative boundary as a starting point.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
For example we have a customer whose sales representatives are deployed on specific zones. Each zone is typically based on a French "département" (an administrative region). It can be a full département, or half a département, or a third...</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
For a bit of fun I wrote <a href="https://gmaps-zone-creator.appspot.com/#/">a quick demo of this approach</a>. The demo application allows you to split a département in two zones. Each zone will be affected to a sales representative.</div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div style="text-align: justify;">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVm1DN8FAe08fdpZjkDz0-TusJ7ExMxn9aBU10q6PP0T7lvLOKxeDYStP4PF1gweLqPMX76LoPaM2oSzSenRVDa4f3us0JR-PnztTeiEHOozcWdwWzcecmZ9YV3p6dSBGx_0yBGpnbX6qp/s1600/2015-01-25_16-24-13.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVm1DN8FAe08fdpZjkDz0-TusJ7ExMxn9aBU10q6PP0T7lvLOKxeDYStP4PF1gweLqPMX76LoPaM2oSzSenRVDa4f3us0JR-PnztTeiEHOozcWdwWzcecmZ9YV3p6dSBGx_0yBGpnbX6qp/s1600/2015-01-25_16-24-13.png" height="640" width="640" /></a></div>
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
You can then use Google Maps' geocoding feature to test any address you want and see if it lands on one of the zones you just created.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
The Github code is available here : <a href="https://github.com/dhatanian/gmaps-zone-creator">https://github.com/dhatanian/gmaps-zone-creator</a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
The demo application is available here : <a href="https://gmaps-zone-creator.appspot.com/">https://gmaps-zone-creator.appspot.com</a></div>Anonymoushttp://www.blogger.com/profile/02803423273909086751noreply@blogger.com0tag:blogger.com,1999:blog-5219665179084602082.post-1863624474559065152014-12-12T01:39:00.000-08:002015-05-15T10:45:41.093-07:00OpenAM and Kerberos authentication : how to provide a fallback for devices who do not have Kerberos enabled ?<script>
location.href="https://david-codes.hatanian.com/2014/12/12/openam-and-kerberos-authentication-how.html";
</script>
<div style="text-align: justify;">
OpenAM is very commonly used with the Kerberos and SPNEGO protocols to provide seamless authentication inside an company's network.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Those are the protocols used by OpenAM's Windows Desktop SSO module. It is extremely convenient : no need to input a password, you are automatically logged in.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
When we deployed Kerberos, we usually face an issue with devices not configured for Kerberos authentication : phones, tablets, macbooks or Windows computers that were not configured by the company's administrators. Those users would see an "HTTP 401" error when attempting to authenticate to Kerberos.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
If you did not change your default error page, it would look like this on Apache Tomcat :</div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilUGeTwV6ABCvgV1AjtOaKgrSo6XLn2rEZqKUrp_EwZj36OVTfnGR90Rw9NbQe80ompQhEdZM7x5IqRXoXRVC7KDJ5lgAi9MSIhVJst8h8GQxzu5pRv5KthdAC30d44Gd6hc2YclbQXOJ2/s1600/2014-12-12_16-12-14.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilUGeTwV6ABCvgV1AjtOaKgrSo6XLn2rEZqKUrp_EwZj36OVTfnGR90Rw9NbQe80ompQhEdZM7x5IqRXoXRVC7KDJ5lgAi9MSIhVJst8h8GQxzu5pRv5KthdAC30d44Gd6hc2YclbQXOJ2/s1600/2014-12-12_16-12-14.png" height="328" width="640" /></a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
The usual workaround is to edit the default 401 error page to redirect the user to a different authentication solution.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
The problem with this method is that the user's original request is lost : OpenAM will not know anymore what application the user wanted to access. The usual solution is either to redirect the user arbitrarily to the most commonly used application, or to display a list of applications for the user to choose from.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
We came around a better way to do this, without losing the user's original request. Here is how it works :</div>
<div style="text-align: justify;">
</div>
<ul>
<li>When the user fails the Kerberos authentication, a custom 401 page is displayed to the user. This 401 page sends the user back to the page he was trying to access (the login form), but with an additional request in the query string.</li>
<li>When the user hits this page, he is redirected to <i>https://sso.company.com/UI/Login?.....&<b>ignoreHttpCallback=true </b>. </i>The last parameter is added by the custom 401 page.</li>
<li>A custom filter added in OpenAM's <i>web.xml</i> detected the <i>ignoreHttpCallback</i> parameter and injects a fake <i>Authorization</i> header into the request, to make OpenAM believe that the client is trying to use Kerberos</li>
<li>With this, the Windows Desktop SSO module is started, and its authentication fails. If you have another module in the authentication chain (typically an Active Directory module), it will be used instead of the Windows Desktop SSO module.</li>
</ul>
<div>
Note that for this workaround to work, you must use an authentication chain containing the Windows Desktop SSO module (in level SUFFICIENT) and another fallback module, such as Active Directory, also in level SUFFICIENT.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3Ov8aG7Umvpi8evV8BmK80IJJgTwNsdgfsq6neyVkK2Wy18lKETeoNFTLcnM9arw-cGt_mWoxww9zGpEIhKH_KGl8J8RDdNcmgsBJ1SRLUO7zBcjpsG4O18PQy_AqwXWSi7BbAEtAZggr/s1600/2014-12-12_16-29-41.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3Ov8aG7Umvpi8evV8BmK80IJJgTwNsdgfsq6neyVkK2Wy18lKETeoNFTLcnM9arw-cGt_mWoxww9zGpEIhKH_KGl8J8RDdNcmgsBJ1SRLUO7zBcjpsG4O18PQy_AqwXWSi7BbAEtAZggr/s1600/2014-12-12_16-29-41.png" height="53" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Here is the code of a simple custom 401 error page :</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<pre class="prettyprint linenums lang-java">
<%@ page language="java" isErrorPage="true" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%
String redirectURL = null;
try {
redirectURL = request.getAttribute("javax.servlet.forward.request_uri") + "?" + request.getAttribute("javax.servlet.forward.query_string") +
"&ignoreHttpCallback=true";
} catch (Exception e) {
throw new RuntimeException("Unable to generate target URL", e);
}
if(!response.containsHeader("WWW-Authenticate")){
response.addHeader("WWW-Authenticate", "Negotiate");
}
%>
<html>
<head>
<meta http-equiv="refresh" content="1; <%=redirectURL%>"/>
</head>
<body>
<h1>Error during transparent authentication.</h1>
<p>You will be automatically redirected to the login/password fallback.</p>
<p>If the redirection does not happen, please <a href="<%=redirectURL%>">click here for manual redirection</a>.</p>
</body>
</html>
</pre>
<br />
And here is the code of the HttpFilter we use to simulate a Kerberos ticket, based on the <i>ignoreHttpCallback </i>parameter :<br />
<br />
<br />
<pre class="prettyprint linenums lang-java">
public class KerberosFallbackFilter implements Filter {
private static final String IGNORE_PARAMETER = "ignoreHttpCallback";
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (request.getParameter(IGNORE_PARAMETER) != null) {
request = new AuthorisationHeaderEnhancedRequest((HttpServletRequest) request);
}
chain.doFilter(request, response);
}
public class AuthorisationHeaderEnhancedRequest extends HttpServletRequestWrapper {
private static final String AUTHORIZATION_HEADER = "Authorization";
private static final String FAKE_HEADER = "Negotiate FAKE_HEADER";
public AuthorisationHeaderEnhancedRequest(HttpServletRequest req) {
super(req);
}
@Override
public String getHeader(String key) {
if (key != null && key.trim().equalsIgnoreCase(AUTHORIZATION_HEADER)) {
return FAKE_HEADER;
} else {
return super.getHeader(key);
}
}
}
}
</pre>Anonymoushttp://www.blogger.com/profile/02803423273909086751noreply@blogger.com0tag:blogger.com,1999:blog-5219665179084602082.post-58969718189551848102014-09-20T01:48:00.001-07:002014-11-24T21:52:11.863-08:00Cloud computing and the tragedy of the commons<div style="text-align: justify;">
It occurred recently to me that Cloud Computing is extremely exposed to an interesting manifestation of the <a href="http://en.wikipedia.org/wiki/Tragedy_of_the_commons">tragedy of the commons</a>.</div>
<div style="text-align: justify;">
<br /></div>
<h2 style="text-align: justify;">
Whazzat ?</h2>
<div style="text-align: justify;">
The tragedy of the commons describes a situation where several parties share a limited resource and, by acting according to their self-interest, actually behave contrary to the whole group's best interest. Traditional examples of the tragedy in the commons include people littering in the street, and more generally any misuse of a public good.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
When you subscribe to an IAAS, PAAS or SAAS service, you will inevitably share a limited resource, which is the time the company in charge of the service can spend on your specific needs.</div>
<h2 style="text-align: justify;">
And it happens all the time</h2>
<div style="text-align: justify;">
To take a concrete example, a customer recently complained that a support ticket we file against a third party PAAS was not progressing fast enough. When we asked the support team for an update, they simply answered "Our engineers are working on it, in the meanwhile here's a workaround [...]".</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Indeed we had already setup the workaround, and it was satisfying enough, so the ticket resolution was not urgent for us. But my customer found their answer "absolutely inacceptable", even after admitting that they were not impacted since we had setup the workaround. He simply could not accept that we had to wait, and wanted the issue dealt with immediately.</div>
<h2 style="text-align: justify;">
Cloud Computing and the tragedy of the commons</h2>
<div style="text-align: justify;">
Since the provider company has a limited amount of engineers to dispatch on problems (not to mention that engineers are not interchangeable), they usually try to deal with tickets that have high impact first, this is usually :</div>
<ul>
<li style="text-align: justify;">Tickets that severely prevent the service to work</li>
<li style="text-align: justify;">Tickets that impact a lot of customers</li>
<li style="text-align: justify;">Tickets that, when solved, could help sign an interesting contract</li>
</ul>
<div style="text-align: justify;">
<br />
Our issue was none of those, and indeed if, as customers, we has a severe blocking issue, I would have preferred they fix it rather than some small issue for which a workaround exist.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Nonetheless, if you remain focused too hard on the problem at hand, and forget the big picture, it is easy to get carried away and <b>demand</b> that you be serviced as you think you're entitled to. Even though that's useless, not efficient, and whatever else.</div>
<br />
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
This issue with software support was here way before Cloud Computing : lots of people use Microsoft Office and if you contact the Office support it makes sense to expect use cases more grave than yours to be fixed first by Microsoft.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
But Cloud Computing makes that issue way worse, for two reasons :</div>
<div style="text-align: justify;">
</div>
<ol>
<li><b>There are more shared resources than before</b> : Sure, when you installed Office, you shared this piece of software with millions of other users around the world. But you installed MS Office on workstations that you owned and managed. And the macros you build on MS office where ones your developped and managed as well. Nowadays all this is more and more externalized, and becomes a common resource that you must share.</li>
<li><b>It is harder to work around the issue by yourself</b> : because you often do not master the systems on which the faulty service is built, it is extremely hard to come up with a dirty, hopefully temporary fix. As a result a lot more issues need to go through the support process. </li>
</ol>
<br />
<div style="text-align: justify;">
I do not believe there is any solution for this. Even more, I do not think we should be looking for a solution. All the point about As-A-Service resources is that you get a better service for a cheaper price. It is obvious that you cannot expect it to be bespoke as well.</div>
Anonymoushttp://www.blogger.com/profile/02803423273909086751noreply@blogger.com0tag:blogger.com,1999:blog-5219665179084602082.post-2108268494843841052014-09-14T07:11:00.001-07:002014-09-14T07:14:20.708-07:00Europe Assistance's poor service<div style="text-align: justify;">
I always regarded the insurance packages provided with credit cards or flight tickets as pure scams that nobody would ever get to work.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
However I own a Visa Premier debit card since it was the cheapest my bank was offering at the time, and I was forced to use their medical assistance service once.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
While I eventually got reimbursed, the whole process took so much time, patience, and resistance to incompetence that I feel the need to write it down somewhere.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
At least in France, Visa Premier's medical assistance service is handled by Europe Assistance.
If you are a customer of Europe Assistance or the owner of a Visa card, this is a warning. I hope you will not experience the same service as I did.
If by any chance someone at Europe Assistance stumbles on this post, please do something to improve your customer service. You are dealing with people who need help (your company name should be a hint) and you cannot have such a subpar communication process.</div>
<div style="text-align: justify;">
<br /></div>
<h2 style="text-align: justify;">
Agents do not read the text of filed requests</h2>
<div style="text-align: justify;">
Getting reimbursed for my fiancée's medical expenses took us 46 days, during which I had several contacts over email and phone with Europe Assistance's agents.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
All of them were very polite, but of a limited professionalism. While I understand that on the phone agents do not have the time to review all the information of a ticket, it should be obvious that when they're using asynchronous communication such as emails they should check the request history first.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
In our case, it was clear that the agents did not bother to check first. While most of the people they deal with is under French healthcare, it is not my fiancée's case. I had to explain this several times to agents who expected me to produce a proof of reimbursement from the French healthcare. Almost every of my early interactions started with "you need to provide a proof of reimbursement", and then I had to explain that it was neither possible nor required.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
The same thing happened with the documents that we were asked to submit, namely a copy of our plane ticket and a copy of my fiancée's passport. I sent both and then called to check that they were received. And the conversation goes :</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Agent - Yes we received you plane ticket, we will start the verification process.</div>
<div style="text-align: justify;">
Me - OK... wait, you have received the plane ticket AND the passport right ?</div>
<div style="text-align: justify;">
Agent - No only the plane ticket, you need to re-send the passport</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Tell me Europe Assistance, how hard is it to show on your agents' computer a small box explaining what is the next step and what is expected from the customer ?</div>
<h2 style="text-align: justify;">
Europe assistance does not understand what asynchronous communication means</h2>
<div style="text-align: justify;">
I mentioned later that I emailed them, then called them. That's how you must work with them. <span style="text-align: justify;">Remember 1995 when you called your mom to tell her to check her emails ? Europe Assistance is blocked on this year...</span></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
There is absolutely no way to confirm that the email was received... in 2014 it seems unbelievable to me that their ticketing system does not automatically confirm that the email was received, but even worst : I sent more than 10 emails ending with something like "please acknowledge reception of this mail". I know that the emails arrived, because I received an answer a few days later, but never, not only once, did an agent bother to reply a one-liner such as "thank you for your message, we will process it in the next few days".</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Seriously, when you use asynchronous communication, please provide peace of mind to your customers by acknowledging their messages.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
You might wonder why I am so fixated on getting my emails acknowledged, but here's the reason...</div>
<h2 style="text-align: justify;">
Emails with big attachments are "lost"</h2>
<div style="text-align: justify;">
Europe Assistance's mail servers have a ridiculous limit on attachment size. I do not remember the exact limit but nothing more than a couple megabytes could be sent. When you know that they ask for the full copy of a passport (yes, all pages, including the empty ones), imagine the consequences. I had to send 40 mails, one per page, to make sure everything reaches this seemingly impermeable wall.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
All email services impose a limit on attachment size, but it is customary when an email is too big to warn the sender by replying with a warning.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Europe Assistance seems to have never heard of this common sense solution. They simply drop the email in some black hole, and nobody ever hears of it, until you call them only to learn that they never received it.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
How is that decent customer service ? How hard would it be to assist the user into sending his documents to their services ? I am a software engineer and when something does not work (like an email that never reaches its destination) I can guess the reason and it is easy enough for me to resize an image, but is it the case for all their audience ?</div>
<h2 style="text-align: justify;">
They do not push the process forward, you have to call them for that</h2>
<div style="text-align: justify;">
This really drives me crazy. Their support system is here for one thing and one thing only : follow the process that they themselves defined.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
During my phone exchanges with their agents, it happened twice that the process was stalled for no apparent reason.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Me - "We have completed the process, sent all the documents, but we are still waiting for the payment"</div>
<div style="text-align: justify;">
Agent - "Ah yes your request is here, let me process it and get back to you"</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Wait, we have been waiting for two weeks while our request was just sitting on your (virtual) desk and you were not processing it ? What if I hadn't called ?</div>
<h2>
They are unable to process a complaint</h2>
<div style="text-align: justify;">
At some point, I complained on twitter, curious to know if there would be a response, not hoping much from a company that still does not master email.</div>
<blockquote class="twitter-tweet" lang="fr">
Europe Assistance really needs to improve their support process ! Acknowledgement of sent emails, follow-up, explain next steps...
<a href="https://twitter.com/EA_FRA">@EA_FRA</a><br />
— David Hatanian (@DHatanian) <a href="https://twitter.com/DHatanian/status/478063511713501185">15 Juin 2014</a></blockquote>
<script async="" charset="utf-8" src="//platform.twitter.com/widgets.js"></script>
So I was amazed to receive an answer a few hours later :<br />
<br />
<blockquote class="twitter-tweet" lang="fr">
<a href="https://twitter.com/DHatanian">@DHatanian</a> Could you send us your phone number by DM ? Our assistance team will call you back. Thank you<br />
— Europ Assistance FR (@EA_FRA) <a href="https://twitter.com/EA_FRA/status/478078242424127488">15 Juin 2014</a></blockquote>
<div style="text-align: justify;">
<script async="" charset="utf-8" src="//platform.twitter.com/widgets.js"></script>
After some complications because their community manager does not know that he needs to follow someone so that the person can DM, I explained my issue, hoping that it would get escalated and that things would get smoother.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
I then got the dreaded answer "I checked your file, and you just need to provide the reimbursement proof from the French healthcare service". Aaaah thank you so much Europ Assistance, I see that you reviewed my situation carefully, especially my messages explaining that my fiancée was not registered under French healthcare.</div>
<h2>
I will never sign up for Europe Assistance again</h2>
<div style="text-align: justify;">
I am lucky that my credit card's assurance was provided by Europe Assistance, because now I know that I will never decide to use their services for me or anyone who is dear to me. I seriously hope that they will improve their service, because my experience with them did not look professional at all.</div>
Anonymoushttp://www.blogger.com/profile/02803423273909086751noreply@blogger.com0tag:blogger.com,1999:blog-5219665179084602082.post-2267404516749248682014-09-10T19:24:00.002-07:002014-09-14T04:33:37.182-07:00Why you should not try to deal with dates manually<div style="text-align: justify;">
When you're writing a program, you will always have to deal with dates and times at one point of the other.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
It could be because you want to setup a scheduled task, or because you want a feature that provides reporting of his last month's activities to the user.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Date handling can be very tricky, and a lot of users still try to handle this manually. If you read <a href="http://thedailywtf.com/">The Daily WTF</a>, you'll notice that probably a third of the poor coding examples have something to do with dates.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Microsoft's Azure platform experienced an <a href="http://azure.microsoft.com/blog/2012/03/09/summary-of-windows-azure-service-disruption-on-feb-29th-2012/">outage last year</a> due to incorrect handling of leap years in 2013.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
This articles is an attempt to list the gotchas you will encounter working with dates. There are two type of tricky aspects in handling dates : the date system itself, with its timezone, leap years and daylight saving time; and the technical aspect, that is how our computer systems manage dates.</div>
<h2>
Timezones and daylight saving time</h2>
<div>
<div style="text-align: justify;">
Aaah timezones... certainly easy to compute, right ? Just a signed <i>int</i> to indicate how many hours from UTC to store in the database.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
But wait, did you know that some timezones have 30 minutes offset as well ? Check out Iran (UTC+4:30) or India (UTC+5:30).</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Countries also change timezones from time to time, usually for economical or political purpose, that is to get closer to a partner country or to show distance with a neighbour a bit too invasive. <a href="http://www.timeanddate.com/news/time/samoa-dateline.html">Samao switched timezone 3 years ago</a> to get closer to Australia.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
What about daylight saving time ? Did you know that some country change the DST at the last minute ? For example, <a href="http://www.timeanddate.com/news/time/egypt-morocco-dst-ramadan-2014.html">Morocco has already done this quite a couple of times</a>, to prepare for the religious month of Ramaddan. At that time, all our clocks on Windows were 1 hour late... If you run a calendar app and provide notification services, your Moroccan users probably received the notifications one hour too late during those days.</div>
<h2 style="text-align: justify;">
Leap years</h2>
<div style="text-align: justify;">
Enough about country-specific aspects, what about universal aspects of our calendar, such as leap years ? In those years, the year counts 366 days instead of the usual 365. Here's how to know if you're in a leap year <a href="http://en.wikipedia.org/wiki/Leap_year#Algorithm">according to Wikipedia</a> :</div>
<blockquote class="tr_bq" style="text-align: justify;">
<b style="background-color: white; color: #252525; font-family: sans-serif; font-size: 14px; line-height: 22.3999996185303px; text-align: start;">if</b><span style="background-color: white; color: #252525; font-family: sans-serif; font-size: 14px; line-height: 22.3999996185303px; text-align: start;"> (</span><i style="background-color: white; color: #252525; font-family: sans-serif; font-size: 14px; line-height: 22.3999996185303px; text-align: start;">year</i><span style="background-color: white; color: #252525; font-family: sans-serif; font-size: 14px; line-height: 22.3999996185303px; text-align: start;"> is not divisible by 4) </span><b style="background-color: white; color: #252525; font-family: sans-serif; font-size: 14px; line-height: 22.3999996185303px; text-align: start;">then</b><span style="background-color: white; color: #252525; font-family: sans-serif; font-size: 14px; line-height: 22.3999996185303px; text-align: start;"> (it is a common year)</span> </blockquote>
<blockquote class="tr_bq" style="text-align: justify;">
<b style="background-color: white; color: #252525; font-family: sans-serif; font-size: 14px; line-height: 22.3999996185303px; text-align: start;">else</b><b style="background-color: white; color: #252525; font-family: sans-serif; font-size: 14px; line-height: 22.3999996185303px; text-align: start;">if</b><span style="background-color: white; color: #252525; font-family: sans-serif; font-size: 14px; line-height: 22.3999996185303px; text-align: start;"> (</span><i style="background-color: white; color: #252525; font-family: sans-serif; font-size: 14px; line-height: 22.3999996185303px; text-align: start;">year</i><span style="background-color: white; color: #252525; font-family: sans-serif; font-size: 14px; line-height: 22.3999996185303px; text-align: start;"> is not divisible by 100) </span><b style="background-color: white; color: #252525; font-family: sans-serif; font-size: 14px; line-height: 22.3999996185303px; text-align: start;">then</b><span style="background-color: white; color: #252525; font-family: sans-serif; font-size: 14px; line-height: 22.3999996185303px; text-align: start;"> (it is a leap year)</span> </blockquote>
<blockquote class="tr_bq" style="text-align: justify;">
<b style="background-color: white; color: #252525; font-family: sans-serif; font-size: 14px; line-height: 22.3999996185303px; text-align: start;">else</b><b style="background-color: white; color: #252525; font-family: sans-serif; font-size: 14px; line-height: 22.3999996185303px; text-align: start;">if</b><span style="background-color: white; color: #252525; font-family: sans-serif; font-size: 14px; line-height: 22.3999996185303px; text-align: start;"> (</span><i style="background-color: white; color: #252525; font-family: sans-serif; font-size: 14px; line-height: 22.3999996185303px; text-align: start;">year</i><span style="background-color: white; color: #252525; font-family: sans-serif; font-size: 14px; line-height: 22.3999996185303px; text-align: start;"> is not divisible by 400) </span><b style="background-color: white; color: #252525; font-family: sans-serif; font-size: 14px; line-height: 22.3999996185303px; text-align: start;">then</b><span style="background-color: white; color: #252525; font-family: sans-serif; font-size: 14px; line-height: 22.3999996185303px; text-align: start;"> (it is a common year)</span> </blockquote>
<blockquote class="tr_bq" style="text-align: justify;">
<b style="background-color: white; color: #252525; font-family: sans-serif; font-size: 14px; line-height: 22.3999996185303px; text-align: start;">else</b><span style="background-color: white; color: #252525; font-family: sans-serif; font-size: 14px; line-height: 22.3999996185303px; text-align: start;"> (it is a leap year)</span></blockquote>
If Microsoft made the mistake, we can expect others to do it as well.<br />
<br />
<div style="text-align: justify;">
Now, let us say you have circumvented the problem by using a reliable date library (more on that later) and good unit testing. You're not done yet because our computer systems make date management even trickier...</div>
</div>
<h2>
Trusting the user's clock</h2>
<div>
If you're developing a rich web application, a mobile application or an old school desktop application, you will have to deal with the user's clock.<br />
<br />
<div style="text-align: justify;">
That clock can be improperly set, and your time-sensitive operations might fail because of this. Let us say you have a javascript application that can ask a server for data at a certain time. To stay in the calendar example, let us imagine that the client code retrieves a list of events for a given set of start and end date.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
If you client's clock is wrong, you will end up requesting the list of events for yesterday when the user wants the list for tomorrow.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
One very time-sensitive type of operations is authentication. A lot of authentication protocols use timestamps (OAuth, Kerberos for example) and the authentication will fail if the client's clock is set wrong.</div>
</div>
<h2>
Parsing and printing dates</h2>
Did you write any application that does not either parse a date or prints somewhere (screen, name of a file, a web-service) ?<br />
<br />
<div style="text-align: justify;">
Try as much as possible to avoid parsing dates that are locale-dependent. Something that is written "Tuesday, September 3rd" might be written "Mardi 3 Septembre" on another machine.</div>
<br />
<div style="text-align: justify;">
Also, if you're displaying dates in a user interface, take into account that the length of month names depend on the language : July and Juillet are the same month but do not have the same length. Beware of text that does not fit and overflows or becomes partly hidden.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
When parsing/printing a date, do not forget the TimeZone, otherwise you'll be off by several hours.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
My recommendations :</div>
<div style="text-align: justify;">
</div>
<ul>
<li>Use ms or ns since epoch when for technical purposes (web-services, storing in a file etc)</li>
<li>When you still want the date to be readable by the user, use the <a href="http://en.wikipedia.org/wiki/ISO_8601">ISO 8601 format</a> that is easier to parse and alphabetically sorted</li>
</ul>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://imgs.xkcd.com/comics/iso_8601.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://imgs.xkcd.com/comics/iso_8601.png" height="320" width="274" /></a></div>
<br />
<h2>
Who will save us ?</h2>
<div>
In Java, the <i>java.util.Date </i>and <i>java.util.Calendar</i> API is good enough for good for most simple uses. The <a href="http://www.joda.org/joda-time">Joda time library</a> gives easiest access to most date and time-related operations.<br />
<br />
The <a href="http://www.joda.org/joda-time/#Why_Joda-Time">"Why Joda Time"</a> section of the above link summarizes the advantages of this library as follows :<br />
<br />
<blockquote class="tr_bq">
<ul style="background-color: white; font-family: Helvetica, Arial, sans-serif; font-size: 13px; margin-bottom: 6px; margin-top: 4px;">
<li><b>Easy to Use</b>. Calendar makes accessing 'normal' dates difficult, due to the lack of simple methods. Joda-Time has straightforward <a href="http://www.joda.org/joda-time/field.html" style="color: #000099; text-decoration: none;">field accessors</a> such as <tt>getYear()</tt> or <tt>getDayOfWeek()</tt>.</li>
<li><b>Easy to Extend</b>. The JDK supports multiple calendar systems via subclasses of <tt>Calendar</tt>. This is clunky, and in practice it is very difficult to write another calendar system. Joda-Time supports multiple calendar systems via a pluggable system based on the <tt>Chronology</tt> class.</li>
<li><b>Comprehensive Feature Set</b>. The library is intended to provide all the functionality that is required for date-time calculations. It already provides out-of-the-box features, such as support for oddball date formats, which are difficult to replicate with the JDK.</li>
<li><b>Up-to-date Time Zone calculations</b>. The <a href="http://www.joda.org/joda-time/timezones.html" style="color: #000099; text-decoration: none;">time zone implementation</a> is based on the public <a class="externalLink" href="http://www.iana.org/time-zones" style="background: url(http://www.joda.org/joda-time/images/external.png) 100% 50% no-repeat; color: #000099; padding-right: 15px; text-decoration: none;">tz database</a>, which is updated several times a year. New Joda-Time releases incorporate all changes made to this database. Should the changes be needed earlier, <a href="http://www.joda.org/joda-time/tz_update.html" style="color: #000099; text-decoration: none;">manually updating the zone data</a> is easy.</li>
<li><b>Calendar support</b>. The library currently provides 8 calendar systems. More will be added in the future.</li>
<li><b>Easy interoperability</b>. The library internally uses a millisecond instant which is identical to the JDK and similar to other common time representations. This makes interoperability easy, and Joda-Time comes with out-of-the-box JDK interoperability.</li>
<li><b>Better Performance Characteristics</b>. Calendar has strange performance characteristics as it recalculates fields at unexpected moments. Joda-Time does only the minimal calculation for the field that is being accessed.</li>
<li><b>Good Test Coverage</b>. Joda-Time has a comprehensive set of developer tests, providing assurance of the library's quality.</li>
<li><b>Complete Documentation</b>. There is a full <a href="http://www.joda.org/joda-time/userguide.html" style="color: #000099; text-decoration: none;">User Guide</a> which provides an overview and covers common usage scenarios. The <a href="http://www.joda.org/joda-time/apidocs/index.html" style="color: #000099; text-decoration: none;">javadoc</a> is extremely detailed and covers the rest of the API.</li>
<li><b>Maturity</b>. The library has been under active development since 2002. Although it continues to be improved with the addition of new features and bug-fixes, it is a mature and reliable code base. A number of <a href="http://www.joda.org/joda-time/related.html" style="color: #000099; text-decoration: none;">related projects</a> are now available.</li>
<li><b>Open Source</b>. Joda-Time is licenced under the business friendly <a href="http://www.joda.org/joda-time/license.html" style="color: #000099; text-decoration: none;">Apache License Version 2.0</a>.</li>
</ul>
</blockquote>
<br /></div>
<div>
Note that since Java 8, a <a href="http://java.dzone.com/articles/introducing-new-date-and-time">new Date and Time API</a> has been introduced in the JDK, and its creation involved the author of the Joda Time library.</div>
Anonymoushttp://www.blogger.com/profile/02803423273909086751noreply@blogger.com0tag:blogger.com,1999:blog-5219665179084602082.post-63989132749455591342014-08-05T04:17:00.004-07:002014-08-05T04:21:05.506-07:00OpenAM and SAML2 federation : returning a different NameID for each Service Provider<div style="text-align: justify;">
If you have an OpenAM identity provider connected to several service providers, chances are that not all providers expect the same NameID. Some, like Google Apps, make ask for the user's email while others will expect something like ActiveDirectory's sAMAccountName.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Now if you're able to map each different NameID to a NameID format in OpenAM, everything will work great for you. Here's an example of NameID mapping configuration based on the NameID format :</div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgXP65KZrBBGxTzGU0LpiUJH75IZmxtYekkr4oAlcuKmqQsIRHIcyaGBAz_3MJcvJk_ZQvvzC_5H-Gku5en_4WjvA8RnEPiXy8X2jWh8eCvM0cKGu2T3Y0aJTvAlsWNeke1K4WgmJ07lmQc/s1600/2014-08-05_17-43-31.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgXP65KZrBBGxTzGU0LpiUJH75IZmxtYekkr4oAlcuKmqQsIRHIcyaGBAz_3MJcvJk_ZQvvzC_5H-Gku5en_4WjvA8RnEPiXy8X2jWh8eCvM0cKGu2T3Y0aJTvAlsWNeke1K4WgmJ07lmQc/s1600/2014-08-05_17-43-31.png" height="180" width="640" /></a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
But what if you're forced to use the NameId called <i>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</i> for all your SPs ? This constraint can come from the SPs, but also from your OpenAM installation. In my case OpenAM has a read-only user store, so the only NameID formats I am allowed to use are the non-persistent ones. And there are only two :
</div>
<br />
<ul>
<li style="text-align: justify;"><i>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</i></li>
<li style="text-align: justify;"><i>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</i></li>
</ul>
<div style="text-align: justify;">
And the <i>transient</i> NameID format is not well managed by some SPs, so I am stuck with the <i>unspecified</i> format.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Luckily, OpenAM provides the ability to inject your own <a href="http://openam.forgerock.org/apidocs/com/sun/identity/saml2/plugins/IDPAccountMapper.html">IDPAccountMapper</a> implementation. You can inject your custom class directly from the interface like this :</div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbdiYnyFSdwAyUx5yNTXuVGakPd7NOh0N-VZBpR-S5xpMkUrl8lpXqvVcA-Sw1VScAMm1l8qGoV1YQeBTQTMWaULZW7KWijuMdcySA6oQcegOP_elf5cF1zmIGOUTqIYWh3ju_iZ11yRGf/s1600/2014-08-05_17-58-08.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbdiYnyFSdwAyUx5yNTXuVGakPd7NOh0N-VZBpR-S5xpMkUrl8lpXqvVcA-Sw1VScAMm1l8qGoV1YQeBTQTMWaULZW7KWijuMdcySA6oQcegOP_elf5cF1zmIGOUTqIYWh3ju_iZ11yRGf/s1600/2014-08-05_17-58-08.png" height="86" width="640" /></a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
I have decided to keep the mapping used in the administration interface, but to allow the user to specify a custom mapping for a given Service Provider. Here's how it looks :</div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4DqNo_686l0AxDaQjnpaXF_FSw96LTSap4oHfepI8wCsskKpTQo0txLXzi3PHfcxXaP2qjix9kpMKQVbKxuqNs7u7HEX61ZlktKqCUv22cpxTCTy7B-rxU_5PWMczp45ZgwkW3vX85lg_/s1600/2014-08-05_17-46-41.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4DqNo_686l0AxDaQjnpaXF_FSw96LTSap4oHfepI8wCsskKpTQo0txLXzi3PHfcxXaP2qjix9kpMKQVbKxuqNs7u7HEX61ZlktKqCUv22cpxTCTy7B-rxU_5PWMczp45ZgwkW3vX85lg_/s1600/2014-08-05_17-46-41.png" height="180" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
In this example, the <i>unspecified</i> NameID format is by default mapped to the <i>mail</i> attribute, unless the SP is <i>https://myserviceprovider.com</i>. In that case, the attribute used will be <i>sAMAccountName</i>.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
I will provide the code for this custom <i>IDPAccountMapper</i>. But this does not work with most SPs, due to a bug in OpenAM (<a href="http://bugster.forgerock.org/jira/browse/OPENAM-4264?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel">OPENAM-4264</a>). As far as I know, this bug exists in all versions of OpenAM at this date.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
The problem is that sometimes the SP name is not provided to the <i>IDPAccountMapper</i>. To solve this, we need to tweak the <i>AuthnRequest</i> object before it is opened by OpenAM to provide the SP name to the <i>IDPAccountMapper</i>.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
For this, we will inject a custom <i>SAML2IdentityProviderAdapter</i>. Again we are lucky since OpenAM allows us to inject an object at the right moment.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbDlSqtwKxOu6EQauwCcYtv46oA1BJ804tGQ35HNc7588LR_KfSvsDKPponB4IRlEJLiSk0DvBlY4kb2ugkqIQY8_vZVvoOX001SCiI6yHkPBBA7XsngPVk897jay2wUuQR76Y5M8iDZfU/s1600/2014-08-05_17-56-41.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbDlSqtwKxOu6EQauwCcYtv46oA1BJ804tGQ35HNc7588LR_KfSvsDKPponB4IRlEJLiSk0DvBlY4kb2ugkqIQY8_vZVvoOX001SCiI6yHkPBBA7XsngPVk897jay2wUuQR76Y5M8iDZfU/s1600/2014-08-05_17-56-41.png" height="82" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Now here's the source code for the <i>IDPAccountMapper </i>and the <i style="text-align: justify;">SAML2IdentityProviderAdapter</i><span style="text-align: justify;"> :</span></div>
<div style="text-align: justify;">
<br /></div>
<pre class="prettyprint">/**
* Copy-paste of the OpenAM DefaultIDPAccountMapper class, with a twist to allow SP-specific configuration.
* To specify a custom attribute for a given SP, use the following mapping : NAMING-FORMAT:SPENTITYID=attribute
* Example : urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified:google.com/a/mycompany.com=mail
*/
public class MyIDPAccountMapper extends DefaultAccountMapper implements IDPAccountMapper {
private DefaultIDPAccountMapper defaultIDPAccountMapperDelegate;
public MyIDPAccountMapper () {
debug.message("MyIDPAccountMapper .constructor");
this.role = "IDPRole";
defaultIDPAccountMapperDelegate = new DefaultIDPAccountMapper();
}
public NameID getNameID(Object session, String hostEntityID, String remoteEntityID, String realm, String nameIDFormat)
throws SAML2Exception {
String userID = null;
try {
SessionProvider sessionProv = SessionManager.getProvider();
userID = sessionProv.getPrincipalName(session);
} catch (SessionException se) {
throw new SAML2Exception(SAML2Utils.bundle.getString("invalidSSOToken"));
}
String nameIDValue = null;
if (nameIDFormat.equals("urn:oasis:names:tc:SAML:2.0:nameid-format:transient")) {
String sessionIndex = IDPSSOUtil.getSessionIndex(session);
if (sessionIndex != null) {
IDPSession idpSession = (IDPSession) IDPCache.idpSessionsByIndices.get(sessionIndex);
if (idpSession != null) {
List list = idpSession.getNameIDandSPpairs();
if ((list != null) && (!list.isEmpty())) {
Iterator iter = list.iterator();
while (iter.hasNext()) {
NameIDandSPpair pair = (NameIDandSPpair) iter.next();
if (pair.getSPEntityID().equals(remoteEntityID)) {
nameIDValue = pair.getNameID().getValue();
break;
}
}
}
}
}
if (nameIDValue == null) {
nameIDValue = getNameIDValueFromUserProfile(realm, hostEntityID, remoteEntityID, userID, nameIDFormat);
if (nameIDValue == null) {
nameIDValue = SAML2Utils.createNameIdentifier();
}
}
} else {
nameIDValue = getNameIDValueFromUserProfile(realm, hostEntityID, remoteEntityID, userID, nameIDFormat);
if (nameIDValue == null) {
if (nameIDFormat.equals("urn:oasis:names:tc:SAML:2.0:nameid-format:persistent")) {
nameIDValue = SAML2Utils.createNameIdentifier();
} else {
throw new SAML2Exception(bundle.getString("unableToGenerateNameIDValue"));
}
}
}
NameID nameID = AssertionFactory.getInstance().createNameID();
nameID.setValue(nameIDValue);
nameID.setFormat(nameIDFormat);
nameID.setNameQualifier(hostEntityID);
nameID.setSPNameQualifier(remoteEntityID);
nameID.setSPProvidedID(null);
return nameID;
}
public String getIdentity(NameID nameID, String hostEntityID, String remoteEntityID, String realm)
throws SAML2Exception {
debug.warning("MyIDPAccountMapper -specific implementation received a call to getIdentity(). This is not supported by this implementation and will be deferred to the DefaultIDPAccountMapper delegate.");
return defaultIDPAccountMapperDelegate.getIdentity(nameID, hostEntityID, remoteEntityID, realm);
}
protected String getNameIDValueFromUserProfile(String realm, String hostEntityID, String remoteEntityID, String userID, String nameIDFormat) {
if (debug.messageEnabled()) {
debug.message("Asking NameID for user " + userID + ", nameId format " + nameIDFormat + ", SP entity : " + remoteEntityID);
}
String nameIDValue = null;
Map formatAttrMap = getFormatAttributeMap(realm, hostEntityID);
String spSpecificNameIDFormat = nameIDFormat + ":" + remoteEntityID;
String attrName = (String) formatAttrMap.get(spSpecificNameIDFormat);
if (attrName == null) {
attrName = (String) formatAttrMap.get(nameIDFormat);
if (debug.messageEnabled()) {
debug.message("Could not find a SP-specific attribute name, found generic attribute name : " + attrName);
}
} else {
if (debug.messageEnabled()) {
debug.message("Found SP-specific attribute name : " + attrName);
}
}
if (attrName != null) {
try {
Set attrValues = dsProvider.getAttribute(userID, attrName);
if ((attrValues != null) && (!attrValues.isEmpty())) {
nameIDValue = (String) attrValues.iterator().next();
}
} catch (DataStoreProviderException dspe) {
if (debug.warningEnabled()) {
debug.warning("DefaultIDPAccountMapper.getNameIDValueFromUserProfile:", dspe);
}
}
}
return nameIDValue;
}
private Map getFormatAttributeMap(String realm, String hostEntityID) {
String key = hostEntityID + "|" + realm;
Map formatAttributeMap = (Map) IDPCache.formatAttributeHash.get(key);
if (formatAttributeMap != null) {
return formatAttributeMap;
}
formatAttributeMap = new HashMap();
List values = SAML2Utils.getAllAttributeValueFromSSOConfig(realm, hostEntityID, this.role, "nameIDFormatMap");
Iterator iter;
if ((values != null) && (!values.isEmpty())) {
for (iter = values.iterator(); iter.hasNext(); ) {
String value = (String) iter.next();
int index = value.indexOf('=');
if (index != -1) {
String format = value.substring(0, index).trim();
String attrName = value.substring(index + 1).trim();
if ((format.length() != 0) && (attrName.length() != 0)) {
formatAttributeMap.put(format, attrName);
}
}
}
}
IDPCache.formatAttributeHash.put(key, formatAttributeMap);
return formatAttributeMap;
}
</pre>
<br/>
<br/>
<br/>
<pre class="prettyprint">/**
* This class is used as a fix for https://bugster.forgerock.org/jira/browse/OPENAM-4264
* We modify the AuthnRequest parameter so that the IDPSSOUtil class can then find the SP id and provide it to the IDPAccountMapper
*/
public class MyAuthRequestUpdatingIDPAdapter extends DefaultIDPAdapter {
private Debugger saml2UtilsDebugger = new SAML2UtilsDebugger();
@Override
public boolean preSendResponse(AuthnRequest authnRequest, String hostProviderID, String realm, HttpServletRequest request, HttpServletResponse response, Object session, String reqID, String relayState) throws SAML2Exception {
SPNameQualifierEnhancer.addSPNameQualifierToAuthnRequest(authnRequest, saml2UtilsDebugger);
return super.preSendResponse(authnRequest, hostProviderID, realm, request, response, session, reqID, relayState);
}
}
</pre>
This is the class that updates the authentication request to fix our bug :
<br />
<pre class="prettyprint">public class SPNameQualifierEnhancer {
private static final String DEBUG_PREFIX = "MyAuthRequestUpdatingIDPAdapter : ";
public static void addSPNameQualifierToAuthnRequest(AuthnRequest authnRequest, Debugger debug) throws SAML2Exception {
if (authnRequest instanceof AuthnRequestImpl) {
MutabilityModifier.makeMutable((AuthnRequestImpl) authnRequest);
if (authnRequest.getNameIDPolicy() == null) {
authnRequest.setNameIDPolicy(new NameIDPolicyImpl());
debug.message(DEBUG_PREFIX + "no NameIDPolicy found in SAML2 authn request, will create a default nameid policy");
}
if (!SPNameQualifierChecker.isValidSPNameQualifier(authnRequest.getNameIDPolicy().getSPNameQualifier())) {
String replacementSpNameQualifier = null;
if (authnRequest.getIssuer() != null) {
replacementSpNameQualifier = authnRequest.getIssuer().getValue();
}
authnRequest.setNameIDPolicy(new NameIDPolicyWithSPNameQualifierProxy(authnRequest.getNameIDPolicy(), replacementSpNameQualifier));
debug.message(DEBUG_PREFIX + "no SPNameQualifier found in SAML2 authn request, will create one with issuer name : " + replacementSpNameQualifier);
}
} else {
debug.warning("Unable to change mutability of class : " + authnRequest.getClass().getCanonicalName());
}
}
}
</pre>
<br/><br/>
This class simply checks whether the SPNameQualifier provided in the authentication request is valid, or if we need to insert one :
<br />
<br />
<pre class="prettyprint">public abstract class SPNameQualifierChecker {
public static boolean isValidSPNameQualifier(String spNameQualifier) {
return spNameQualifier != null && !(spNameQualifier.trim().isEmpty());
}
}
</pre>
<br/><br/>
Since by default the authentication request cannot be modified, we need to tweak it a little bit. Since this implies modifying a <i>protected</i> attribute, <b>the package declaration is important</b> :
<br />
<br />
<pre class="prettyprint">package com.sun.identity.saml2.protocol.impl;
public class MutabilityModifier {
public static void makeMutable(AuthnRequestImpl authnRequest) {
authnRequest.isMutable = true;
}
}
</pre>
<br/><br/>
The rest is just boilerplate :
<br />
<br />
<pre class="prettyprint">public class NameIDPolicyWithSPNameQualifierProxy implements NameIDPolicy {
private NameIDPolicy nameIDPolicy;
private String replacementSPNameQualifier;
public NameIDPolicyWithSPNameQualifierProxy(NameIDPolicy nameIDPolicy, String replacementSPNameQualifier) {
this.nameIDPolicy = nameIDPolicy;
this.replacementSPNameQualifier = replacementSPNameQualifier;
}
@Override
public String getSPNameQualifier() {
return replacementSPNameQualifier;
}
@Override
public String getFormat() {
return nameIDPolicy.getFormat();
}
@Override
public void setFormat(String s) throws SAML2Exception {
nameIDPolicy.setFormat(s);
}
@Override
public void setSPNameQualifier(String s) throws SAML2Exception {
nameIDPolicy.setSPNameQualifier(s);
}
@Override
public void setAllowCreate(boolean b) throws SAML2Exception {
nameIDPolicy.setAllowCreate(b);
}
@Override
public boolean isAllowCreate() {
return nameIDPolicy.isAllowCreate();
}
@Override
public String toXMLString() throws SAML2Exception {
return nameIDPolicy.toXMLString();
}
@Override
public String toXMLString(boolean b, boolean b2) throws SAML2Exception {
return nameIDPolicy.toXMLString(b, b2);
}
@Override
public void makeImmutable() {
nameIDPolicy.makeImmutable();
}
@Override
public boolean isMutable() {
return nameIDPolicy.isMutable();
}
}
</pre>
<br/>
<pre class="prettyprint">public interface Debugger {
public void message(String s);
public void warning(String s);
}
</pre>
<br/>
<pre class="prettyprint">public class SAML2UtilsDebugger implements Debugger{
@Override
public void message(String s) {
SAML2Utils.debug.message(s);
}
@Override
public void warning(String s) {
SAML2Utils.debug.warning(s);
}
}
</pre>
<br/>
And the test classes :
<br />
<br />
<pre class="prettyprint">public class MyAuthRequestUpdatingIDPAdapterTest{
private Debugger debugger = new TestDebugger();
private static final String ISSUER = "theissuer";
private static final String VALID_QUALIFIER = "thequalifier";
@Test
public void testNoNameIDPolicy() throws Exception {
AuthnRequest authnRequest = new AuthnRequestImpl();
Issuer issuer = new IssuerImpl();
issuer.setValue(ISSUER);
authnRequest.setIssuer(issuer);
authnRequest.makeImmutable();
SPNameQualifierEnhancer.addSPNameQualifierToAuthnRequest(authnRequest,debugger );
assertNotNull(authnRequest.getNameIDPolicy());
assertEquals(NameIDPolicyWithSPNameQualifierProxy.class, authnRequest.getNameIDPolicy().getClass());
assertEquals(ISSUER, authnRequest.getNameIDPolicy().getSPNameQualifier());
}
@Test
public void testNoSPNameQualifier() throws Exception {
AuthnRequest authnRequest = new AuthnRequestImpl();
Issuer issuer = new IssuerImpl();
issuer.setValue(ISSUER);
authnRequest.setIssuer(issuer);
NameIDPolicy nameIDPolicy = new NameIDPolicyImpl();
nameIDPolicy.makeImmutable();
authnRequest.setNameIDPolicy(nameIDPolicy);
authnRequest.makeImmutable();
SPNameQualifierEnhancer.addSPNameQualifierToAuthnRequest(authnRequest, debugger);
assertNotNull(authnRequest.getNameIDPolicy());
assertEquals(NameIDPolicyWithSPNameQualifierProxy.class, authnRequest.getNameIDPolicy().getClass());
assertEquals(ISSUER, authnRequest.getNameIDPolicy().getSPNameQualifier());
}
@Test
public void testEmptySPNameQualifier() throws Exception {
AuthnRequest authnRequest = new AuthnRequestImpl();
Issuer issuer = new IssuerImpl();
issuer.setValue(ISSUER);
authnRequest.setIssuer(issuer);
NameIDPolicy nameIDPolicy = new NameIDPolicyImpl();
nameIDPolicy.setSPNameQualifier(" ");
nameIDPolicy.makeImmutable();
authnRequest.setNameIDPolicy(nameIDPolicy);
authnRequest.makeImmutable();
SPNameQualifierEnhancer.addSPNameQualifierToAuthnRequest(authnRequest, debugger);
assertNotNull(authnRequest.getNameIDPolicy());
assertEquals(NameIDPolicyWithSPNameQualifierProxy.class, authnRequest.getNameIDPolicy().getClass());
assertEquals(ISSUER, authnRequest.getNameIDPolicy().getSPNameQualifier());
}
@Test
public void testValidSPNameQualifier() throws Exception {
AuthnRequest authnRequest = new AuthnRequestImpl();
Issuer issuer = new IssuerImpl();
issuer.setValue(ISSUER);
authnRequest.setIssuer(issuer);
NameIDPolicy nameIDPolicy = new NameIDPolicyImpl();
nameIDPolicy.setSPNameQualifier(VALID_QUALIFIER);
nameIDPolicy.makeImmutable();
authnRequest.setNameIDPolicy(nameIDPolicy);
authnRequest.makeImmutable();
SPNameQualifierEnhancer.addSPNameQualifierToAuthnRequest(authnRequest, debugger);
assertNotNull(authnRequest.getNameIDPolicy());
assertEquals(NameIDPolicyImpl.class, authnRequest.getNameIDPolicy().getClass());
assertEquals(VALID_QUALIFIER, authnRequest.getNameIDPolicy().getSPNameQualifier());
}
}
</pre>
<br/><br/>
<pre class="prettyprint">public class TestDebugger implements Debugger {
@Override
public void message(String s) {
System.out.println("MESSAGE : "+s);
}
@Override
public void warning(String s) {
System.out.println("WARNING : "+s);
}
}
</pre>Anonymoushttp://www.blogger.com/profile/02803423273909086751noreply@blogger.com0tag:blogger.com,1999:blog-5219665179084602082.post-57875113351709036412014-07-22T03:50:00.000-07:002014-10-02T21:21:09.871-07:00Google APIs : checking the scopes contained in an OAuth2 access tokenWhen you've stored an OAuth2 access/refresh token couple for a long time, you might not be sure what scopes it was giving access to.<br />
<br />
In that case, just pass the access token to the <i>tokeninfo</i> endpoint :<br />
<br />
<pre class="prettyprint">https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=XXXXX
</pre>
<br />
The output looks like this :<br />
<br />
<pre class="default prettyprint prettyprinted" style="background: rgb(238, 238, 238); border: 0px; font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif; font-size: 14px; line-height: 17.8048000335693px; margin-bottom: 10px; max-height: 600px; overflow: auto; padding: 5px; vertical-align: baseline; width: auto; word-wrap: normal;"><code style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif; margin: 0px; padding: 0px; vertical-align: baseline; white-space: inherit;"><span class="pun" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;">{</span><span class="pln" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;">
</span><span class="str" style="background: transparent; border: 0px; color: maroon; margin: 0px; padding: 0px; vertical-align: baseline;">"issued_to"</span><span class="pun" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;">:</span><span class="pln" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;"> </span><span class="str" style="background: transparent; border: 0px; color: maroon; margin: 0px; padding: 0px; vertical-align: baseline;">"407408718192.apps.googleusercontent.com"</span><span class="pun" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;">,</span><span class="pln" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;">
</span><span class="str" style="background: transparent; border: 0px; color: maroon; margin: 0px; padding: 0px; vertical-align: baseline;">"audience"</span><span class="pun" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;">:</span><span class="pln" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;"> </span><span class="str" style="background: transparent; border: 0px; color: maroon; margin: 0px; padding: 0px; vertical-align: baseline;">"407408718192.apps.googleusercontent.com"</span><span class="pun" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;">,</span><span class="pln" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;">
</span><span class="str" style="background: transparent; border: 0px; color: maroon; margin: 0px; padding: 0px; vertical-align: baseline;">"user_id"</span><span class="pun" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;">:</span><span class="pln" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;"> </span><span class="str" style="background: transparent; border: 0px; color: maroon; margin: 0px; padding: 0px; vertical-align: baseline;">"1170123456778279183758"</span><span class="pun" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;">,</span><span class="pln" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;">
</span><span class="str" style="background: transparent; border: 0px; color: maroon; margin: 0px; padding: 0px; vertical-align: baseline;">"scope"</span><span class="pun" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;">:</span><span class="pln" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;"> </span><span class="str" style="background: transparent; border: 0px; color: maroon; margin: 0px; padding: 0px; vertical-align: baseline;">"https://www.googleapis.com/auth/userinfo.email"</span><span class="pun" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;">,</span><span class="pln" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;">
</span><span class="str" style="background: transparent; border: 0px; color: maroon; margin: 0px; padding: 0px; vertical-align: baseline;">"expires_in"</span><span class="pun" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;">:</span><span class="pln" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;"> </span><span class="lit" style="background: transparent; border: 0px; color: maroon; margin: 0px; padding: 0px; vertical-align: baseline;">3585</span><span class="pun" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;">,</span><span class="pln" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;">
</span><span class="str" style="background: transparent; border: 0px; color: maroon; margin: 0px; padding: 0px; vertical-align: baseline;">"email"</span><span class="pun" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;">:</span><span class="pln" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;"> </span><span class="str" style="background: transparent; border: 0px; color: maroon; margin: 0px; padding: 0px; vertical-align: baseline;">"someone@yourdomain.com"</span><span class="pun" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;">,</span><span class="pln" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;">
</span><span class="str" style="background: transparent; border: 0px; color: maroon; margin: 0px; padding: 0px; vertical-align: baseline;">"verified_email"</span><span class="pun" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;">:</span><span class="pln" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;"> </span><span class="kwd" style="background: transparent; border: 0px; color: darkblue; margin: 0px; padding: 0px; vertical-align: baseline;">true</span><span class="pun" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;">,</span><span class="pln" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;">
</span><span class="str" style="background: transparent; border: 0px; color: maroon; margin: 0px; padding: 0px; vertical-align: baseline;">"access_type"</span><span class="pun" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;">:</span><span class="pln" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;"> </span><span class="str" style="background: transparent; border: 0px; color: maroon; margin: 0px; padding: 0px; vertical-align: baseline;">"offline"</span><span class="pln" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;">
</span><span class="pun" style="background: transparent; border: 0px; margin: 0px; padding: 0px; vertical-align: baseline;">}</span></code></pre>
<br />
Of course you can also do this with a library. In Java :<br />
<br />
<pre class="prettyprint">Oauth2 oauth2 = new Oauth2.Builder(new NetHttpTransport(), new JacksonFactory(), null)
.setApplicationName(ProbeClient.APPLICATION_NAME)
.build();
return oauth2.tokeninfo().setAccessToken(yourAccessToken).execute();
</pre>
<br />
You'll need the following dependency :<br />
<br />
<pre class="prettyprint"><dependency>
<groupId>com.google.apis</groupId>
<artifactId>google-api-services-oauth2</artifactId>
<version>v1-rev76-1.18.0-rc</version>
</dependency>
</pre>
Anonymoushttp://www.blogger.com/profile/02803423273909086751noreply@blogger.com0tag:blogger.com,1999:blog-5219665179084602082.post-29736127742871877852014-07-03T04:18:00.001-07:002015-04-21T03:11:41.594-07:00How to provide seamless Single Sign On on Google Apps<h1 dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 10pt;">
<span style="background-color: transparent; color: black; font-family: 'Trebuchet MS'; font-size: 21px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Context</span></h1>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Before OAuth, in order to authenticate a user an application would redirect him to the </span><a href="https://developers.google.com/accounts/docs/OpenID" style="text-decoration: none;"><span style="background-color: transparent; color: #1155cc; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">OpenID</span></a><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> login page.</span></div>
<b id="docs-internal-guid-4d9bc530-fbeb-b258-e8d0-0a859f204f0d" style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">It was possible to avoid the consent window by registering your the application through the </span><a href="https://developers.google.com/google-apps/extensions-console/?csw=1" style="text-decoration: none;"><span style="background-color: transparent; color: #1155cc; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">Google Apps Console</span></a><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">However, </span><a href="https://developers.google.com/+/api/auth-migration#timetable" style="text-decoration: none;"><span style="background-color: transparent; color: #1155cc; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">OpenID is going to be deprecated</span></a><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> and replaced by OAuth2. This document details how to achieve seamless Single Sign On (no consent window) with OAuth2 through the GApps Marketplace.</span></div>
<h1 dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 10pt;">
<span style="background-color: transparent; color: black; font-family: 'Trebuchet MS'; font-size: 21px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">OAuth authentication flow</span></h1>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">1. Redirect the user to the following URL : </span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> https://accounts.google.com/o/oauth2/auth ?client_id=815629710953-nd6a8ofur4prtau84mt67r9h25hvkd4d.apps.googleusercontent.com</span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> &response_type=code</span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> &scope=email+profile</span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> &redirect_uri=https://localhost:8080</span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> &state=12345678</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">This is standard OAuth behavior, here are the different parts :</span></div>
<ul style="margin-bottom: 0pt; margin-top: 0pt;">
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">client_id : a client_id generated in the API Console</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">response_type : always keep "code"</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">scope : keep "email+profile", otherwise the seamless SSO does not work</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">redirect_uri : URL of your application</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">state : a random string generated by your app to ensure that nobody is stealing the account info</span></div>
</li>
</ul>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">2. Google will bypass the consent window and send the user to the redirect_uri, passing in parameters the same state and the code. In our example :</span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> </span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">https://localhost:8080/?state=12345678&code=4/PE4pFdMWdSU89L5BxCfQYl7rrCe4.sg3ll1ncyWAadJfo-QBMszv26aO4jQI</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">3. Check that the state is valid, typically by checking with a value generated from the user's session. Exchange the code against a refresh and access token. This is standard OAuth behavior, executed server side, here is the request :</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">HTTP POST to https://accounts.google.com/o/oauth2/token HTTP/1.1</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">code=4%2FNa7-XZQEVy4rSpoWHI0g-eanF2pW.os7UPIcmZBUWdJfo-QBMszs6qtm4jQI&redirect_uri=https%3A%2F%2Flocalhost:8080&client_id=815629710953-nd6a8ofur4prtau84mt67r9h25hvkd4d.apps.googleusercontent.com&scope=&client_secret=YOURCLIENTSECRET&grant_type=authorization_code</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">4. Here's a typical answer from Google :</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">{</span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "access_token": "ya29.MQ...UlmQ7g", </span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "token_type": "Bearer", </span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "expires_in": 3600, </span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "refresh_token": "1/V09...4C8", </span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "id_token": "eyJhbGciOi...WlOvPsnHeBEuChaeCziau_MggFWoBCuozy0ZoBdXXc"</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">}</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">From this answer, the best option is to JWT-decode the "id_token". You can use this online tool for testing. Here's the result : </span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">{</span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "sub": "109177816070718727151", </span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "cid": "407408718192.apps.googleusercontent.com", </span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "iss": "accounts.google.com", </span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "email_verified": true, </span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "id": "109177816070718727151", </span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "at_hash": "rAxrTmUjD7rk-5H2EJFnZw", </span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "exp": 1403860943, </span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "azp": "407408718192.apps.googleusercontent.com", </span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "iat": 1403857043, </span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "verified_email": true, </span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "token_hash": "rAxrTmUjD7rk-5H2EJFnZw", </span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "email": "david.hatanian@revevol.eu", </span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "hd": "revevol.eu", </span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "aud": "407408718192.apps.googleusercontent.com"</span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">}</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt; text-align: justify;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">There you go ! you have the email of the user, and in "id" this is a unique id internal to Google, that will never change even if the email changes. It's also the G+ id for G+ users. Now you can store that in the user's session.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<h1 dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 10pt;">
<span style="background-color: transparent; color: black; font-family: 'Trebuchet MS'; font-size: 21px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Configuration steps</span></h1>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Here are all the required setup steps to perform the authentication process described above :</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<ol style="margin-bottom: 0pt; margin-top: 0pt;">
<li dir="ltr" style="background-color: white; color: #222222; font-family: Arial; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; list-style-type: decimal; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Create an API project through the API Console</span></div>
</li>
<li dir="ltr" style="background-color: white; color: #222222; font-family: Arial; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; list-style-type: decimal; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Enable the G+ API and the Marketplace SDK</span></div>
</li>
</ol>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlolRMh73BknZJEwy9lUqNeh0YUzXQ-0Ik1W5C0f6qZ-qMSHQX-U_IjGhV8QEOK0qIGFTzO6UgPpjgm8rB-Wd408igPGpV3do9yHHRGmbN27LFcZStya8b5ci1QZjYIb2c_RH-XfGJheCi/s1600/marketplace.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlolRMh73BknZJEwy9lUqNeh0YUzXQ-0Ik1W5C0f6qZ-qMSHQX-U_IjGhV8QEOK0qIGFTzO6UgPpjgm8rB-Wd408igPGpV3do9yHHRGmbN27LFcZStya8b5ci1QZjYIb2c_RH-XfGJheCi/s1600/marketplace.png" height="339" width="640" /></a></div>
<span id="goog_1608642334"></span><span id="goog_1608642335"></span><br /></div>
<b style="font-weight: normal;"><br /></b>
<br />
<ol start="3" style="margin-bottom: 0pt; margin-top: 0pt;">
<li dir="ltr" style="background-color: white; color: #222222; font-family: Arial; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; list-style-type: decimal; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Generate an OAuth2 webapp credential. Put your application URL as a registered redirect URI.</span></div>
</li>
</ol>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiy0M7DSxcxumzLP0xa7KmKN5XzvSV9zfJeDaV9PAwxYCwqxKg3p8G380vBkkve5ZoQxZm2lqGqxho5cZAAF6pX4JCDa-lm_t49QgCH3Di62ussHCjPlVgHZ2w7t_oa0BcvVLJbMNOsly7h/s1600/cred.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiy0M7DSxcxumzLP0xa7KmKN5XzvSV9zfJeDaV9PAwxYCwqxKg3p8G380vBkkve5ZoQxZm2lqGqxho5cZAAF6pX4JCDa-lm_t49QgCH3Di62ussHCjPlVgHZ2w7t_oa0BcvVLJbMNOsly7h/s1600/cred.png" height="220" width="640" /></a></div>
<br /></div>
<b style="font-weight: normal;"><br /></b>
<br />
<ol start="4" style="margin-bottom: 0pt; margin-top: 0pt;">
<li dir="ltr" style="background-color: white; color: #222222; font-family: Arial; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; list-style-type: decimal; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Setup the marketplace SDK. (click on the cog wheel next to the Marketplace SDK name) You must provide icons and basic info on your app. You will need to choose either "Universal Navigation Link" or "Drive App". See below for the pros and cons, but we recommend "Universal Navigation Link".</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br class="kix-line-break" /></span></div>
</li>
</ol>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjz_lWZuzMTeSrEgRIGxN7R_Rt1b6AJXyPtXxZ3EVUOJs8mxN5HvjmObqOJOaYB52gWjVn3NR1t2q-akllwLZ6ZPHnWqH6nm1Ps_Yh9oFAeyaKcF_rJ9A36buK2k82bPiEMA8iAWsTxj2_U/s1600/sdkconfig.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjz_lWZuzMTeSrEgRIGxN7R_Rt1b6AJXyPtXxZ3EVUOJs8mxN5HvjmObqOJOaYB52gWjVn3NR1t2q-akllwLZ6ZPHnWqH6nm1Ps_Yh9oFAeyaKcF_rJ9A36buK2k82bPiEMA8iAWsTxj2_U/s1600/sdkconfig.png" height="340" width="640" /></a></div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4EWqldwNQyb8CGYuNVO9UZxbfR4UL5K9ccs8waD-Kkfl7aAsQaryU789oy72y4njLOn2IbITF50AH_BuQdPCqk_uIb4XLv0YYG2UrQNUt0vrbo5XNcDF7W-Yq_JdTIo0zuIqL502BmtaX/s1600/2014-09-16_22-59-28.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4EWqldwNQyb8CGYuNVO9UZxbfR4UL5K9ccs8waD-Kkfl7aAsQaryU789oy72y4njLOn2IbITF50AH_BuQdPCqk_uIb4XLv0YYG2UrQNUt0vrbo5XNcDF7W-Yq_JdTIo0zuIqL502BmtaX/s1600/2014-09-16_22-59-28.png" /></a></div>
<b style="font-weight: normal;"><br /></b>
<br />
<b style="font-weight: normal;"><br /></b>
<br />
<ol start="5" style="margin-bottom: 0pt; margin-top: 0pt;">
<li dir="ltr" style="background-color: white; color: #222222; font-family: Arial; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; list-style-type: decimal; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Go to the </span><a href="https://chrome.google.com/webstore/developer/dashboard" style="text-decoration: none;"><span style="background-color: transparent; color: #1155cc; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">Chrome Webstore web developer dashboard</span></a><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, and upload a zip file composed of :</span></div>
</li>
<ol style="margin-bottom: 0pt; margin-top: 0pt;">
<li dir="ltr" style="background-color: white; color: #222222; font-family: Arial; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; list-style-type: decimal; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">A manifest.json</span></div>
</li>
<li dir="ltr" style="background-color: white; color: #222222; font-family: Arial; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; list-style-type: decimal; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">16px and 128px icons for your app</span></div>
</li>
</ol>
</ol>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Here's an example of manifest.json :</span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: white; color: #222222; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">{</span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: white; color: #222222; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "name": "Revevol SSO to </span><span style="background-color: #ffffcc; color: #222222; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">marketplace</span><span style="background-color: white; color: #222222; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">",</span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: white; color: #222222; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "version": "0.1",</span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: white; color: #222222; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: #222222; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">"manifest_version": 2,</span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: white; color: #222222; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "description": "Demonstration of the seamless SSO with the </span><span style="background-color: #ffffcc; color: #222222; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">marketplace</span><span style="background-color: white; color: #222222; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> SDK",</span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: white; color: #222222; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "icons": {</span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: white; color: #222222; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: #222222; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "128": "images/pdf-icon-128x128.png",</span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: white; color: #222222; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "16": "images/Pdf_16x16_Crystal_SVG.png"</span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: white; color: #222222; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> },</span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: white; color: #222222; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "container": ["DOMAIN_INSTALLABLE"],</span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: white; color: #222222; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "api_console_project_id": "815629710953",</span><br />
<span style="background-color: white; color: #222222; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "app": {
"launch": {
"web_url": "https://yourapplication.com"
}
}</span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: white; color: #222222; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">}</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<ol start="6" style="margin-bottom: 0pt; margin-top: 0pt;">
<li dir="ltr" style="background-color: white; color: #222222; font-family: Arial; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; list-style-type: decimal; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Limit the access to the marketplace application to a defined list of trusted testers. Publish it. Here is what you can see in the Chrome Webstore developer dashboard after publication :</span></div>
</li>
</ol>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLV51dWZMN0T2Uib4xqyW47nJhM6wArzQ_eUDHSssTgatNC5DvIy-nVxaxuP3dFBQ85UnxTTxiCegjPLxTOP9apGrC0j-qFEwLkk09AJrqjKH73hcOWbIWiRwSRVJ-UhEd5rO6p4fBvaV9/s1600/webstorelisting.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLV51dWZMN0T2Uib4xqyW47nJhM6wArzQ_eUDHSssTgatNC5DvIy-nVxaxuP3dFBQ85UnxTTxiCegjPLxTOP9apGrC0j-qFEwLkk09AJrqjKH73hcOWbIWiRwSRVJ-UhEd5rO6p4fBvaV9/s1600/webstorelisting.png" height="228" width="640" /></a></div>
<br /></div>
<b style="font-weight: normal;"><br /></b>
<br />
<ol start="7" style="margin-bottom: 0pt; margin-top: 0pt;">
<li dir="ltr" style="background-color: white; color: #222222; font-family: Arial; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; list-style-type: decimal; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">After publication (can take up to 60 minutes), </span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: bold; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">as a domain admin</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, install the app from the Chrome Webstore onto the domain by going on the application’s URL in the Chrome Web Store. To do this, click on the “</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: bold; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Integrate with Google</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">” button.</span></div>
</li>
</ol>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3CH1gEQNDz14zlVPcDvR9Got14JZtrlSmQgK2BxN3O0X7i7ggBgCFY179zcmCZU-lm8yoMqgBzQVYLcDg__IymEMFdYkNs5ORP_un46i-cKnsbdER84bSXpi38eXTyfNvVuTE-lAWFaY8/s1600/websoreresult.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3CH1gEQNDz14zlVPcDvR9Got14JZtrlSmQgK2BxN3O0X7i7ggBgCFY179zcmCZU-lm8yoMqgBzQVYLcDg__IymEMFdYkNs5ORP_un46i-cKnsbdER84bSXpi38eXTyfNvVuTE-lAWFaY8/s1600/websoreresult.png" height="224" width="640" /></a></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<div class="separator" style="clear: both; text-align: center;">
</div>
<br /></div>
<b style="font-weight: normal;"><br /></b>
<br />
<h1 dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 10pt;">
<span style="background-color: transparent; color: black; font-family: 'Trebuchet MS'; font-size: 21px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Limitations</span></h1>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: white; color: #222222; font-family: Arial; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">There are two important limitations with this process :</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: white; color: #222222; font-family: Arial; font-size: 13px; font-style: italic; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Iframes : </span><span style="background-color: white; color: #222222; font-family: Arial; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">You cannot redirect to the OAuth screen in an iframe (like in a sites gadget). In this case you will have to display a button to the users, on which they will have to click. The button will open an authentication popup, the user will be seamlessly authenticated. Then your javascript code must automatically close the popup and refresh the iframe.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: white; color: #222222; font-family: Arial; font-size: 13px; font-style: italic; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">More apps menu</span><span style="background-color: white; color: #222222; font-family: Arial; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> : When setting up the </span><span style="background-color: #ffffcc; color: #222222; font-family: Arial; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">marketplace</span><span style="background-color: white; color: #222222; font-family: Arial; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> SDK, you have the choice between "Navigation link" and "Drive App". If you choose "Navigation Link" the app will show in the "More" menu on the top right. If you choose the "Drive App" then the app will show in the "Manage Apps" menu of Drive. We haven't found a way yet to remove all mentions of the app, and have reported a feature request to Google for this.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt; text-align: center;">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVHYO2zrQzwmPwchytMQKr8khCqq8BR9UTbbtIzoKWpzhGknvLD8zwvvnyI0wIssg69PVHl-J6stjLxZYdtXq0oY3iO7_3DqBZfc6OQ91Jderjr2Tv-HHXcksOSbCu0PV6r8td1oHUWWdB/s1600/more.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVHYO2zrQzwmPwchytMQKr8khCqq8BR9UTbbtIzoKWpzhGknvLD8zwvvnyI0wIssg69PVHl-J6stjLxZYdtXq0oY3iO7_3DqBZfc6OQ91Jderjr2Tv-HHXcksOSbCu0PV6r8td1oHUWWdB/s1600/more.png" height="320" width="235" /></a></div>
<br /></div>
<b style="font-weight: normal;"><br /></b>
<br />
<h1 dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 10pt;">
<span style="background-color: transparent; color: black; font-family: 'Trebuchet MS'; font-size: 21px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Example project</span></h1>
<h2 dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 10pt;">
<span style="background-color: transparent; color: black; font-family: 'Trebuchet MS'; font-size: 17px; font-style: normal; font-variant: normal; font-weight: bold; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Testing</span></h2>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">An example project is available </span><span style="font-family: Arial; font-size: 15px; line-height: 1.15; vertical-align: baseline; white-space: pre-wrap;">on Github here : </span><span style="color: #1155cc; font-family: Arial; font-size: 15px; line-height: 1.15; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;"><a href="https://github.com/dhatanian/demo-marketplace-sso" style="font-family: Arial; font-size: 15px; line-height: 1.15; text-decoration: none;">https://github.com/dhatanian/demo-marketplace-sso</a></span><br />
<br /></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">This project contains the source code for a very simple Java App Engine web application that displays the email of the user :</span></div>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitRUvyw3ds29RWCT_mtRJUTemvTBSNnvGarztfLsaelQKix29xECbTSKcstyzrNid50HZ0lHPl1OxtiSbQacpmfEp-tf22WwCW02pKuV1RUHwSQo_HVXN1ITc3JjyvpWDaYYsh9ayVth6Y/s1600/demo.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitRUvyw3ds29RWCT_mtRJUTemvTBSNnvGarztfLsaelQKix29xECbTSKcstyzrNid50HZ0lHPl1OxtiSbQacpmfEp-tf22WwCW02pKuV1RUHwSQo_HVXN1ITc3JjyvpWDaYYsh9ayVth6Y/s1600/demo.png" height="160" width="640" /></a></div>
<br /></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">It is deployed on App Engine on this URL : </span><a href="https://revevol-marketplace-sso-demo.appspot.com/secured" style="text-decoration: none;"><span style="background-color: transparent; color: #1155cc; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">https://revevol-marketplace-sso-demo.appspot.com/secured</span></a></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">To experience seamless SSO, remember to install the app on your domain through the Chrome Web Store before accessing the URL above. Here’s the app’s URL in the Chrome Web Store : </span><a href="https://chrome.google.com/webstore/detail/demo-of-sso-to-marketplac/fajllpdcdhkpkhnhknjnkfignojcbojd" style="text-decoration: none;"><span style="background-color: transparent; color: #1155cc; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">https://chrome.google.com/webstore/detail/demo-of-sso-to-marketplac/fajllpdcdhkpkhnhknjnkfignojcbojd</span></a></div>
<h2 dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 10pt;">
<span style="background-color: transparent; color: black; font-family: 'Trebuchet MS'; font-size: 17px; font-style: normal; font-variant: normal; font-weight: bold; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">To compile the sample project</span></h2>
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">To compile and deploy this project, you will need Maven. Maven is the build tool in charge of retrieving all the required libraries and compiling the project for us.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<a href="http://maven.apache.org/download.html" style="text-decoration: none;"><span style="background-color: transparent; color: #1155cc; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">You can download Maven here</span></a><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, then follow those </span><a href="http://maven.apache.org/download.cgi#Installation" style="text-decoration: none;"><span style="background-color: transparent; color: #1155cc; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">instructions to complete the installation</span></a><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">After the installation of Maven, you can run the following commands :</span></div>
<br />
<ul style="margin-bottom: 0pt; margin-top: 0pt;">
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">To deploy the project on a server local to your workstation : </span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">mvn appengine:devserver</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">To deploy the project on App Engine : </span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">mvn appengine:update</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> .</span></div>
</li>
<ul style="margin-bottom: 0pt; margin-top: 0pt;">
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; list-style-type: circle; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">This will update the default revevol-marketplace-sso-demo application.</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; list-style-type: circle; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">But you can specify a different one like this : </span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">mvn appengine:update -Dappengine.appId=myappid</span></div>
</li>
</ul>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">To generate an Eclipse project : </span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">mvn eclipse:eclipse</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> .</span></div>
</li>
<ul style="margin-bottom: 0pt; margin-top: 0pt;">
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; list-style-type: circle; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Maven will generate the required Eclipse project files</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; list-style-type: circle; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">After this, you can use the “File > Import” feature of Eclipse to import your project</span></div>
</li>
</ul>
</ul>
Anonymoushttp://www.blogger.com/profile/02803423273909086751noreply@blogger.com14tag:blogger.com,1999:blog-5219665179084602082.post-60828004490890879422014-06-26T21:51:00.001-07:002014-06-26T23:05:28.783-07:00Playing with the GMail API : a chrome extension to convert your emails to PDFGoogle announced yesterday their <a href="https://developers.google.com/gmail/">GMail API</a>. With this API, you can access the user's emails, drafts, attachments etc, as always under OAuth access control.<br />
<br />
I've written a small demo of a Chrome extension that converts your emails to PDF, and creates a new draft with the PDF in attachment. It uses the <i>chrome.identity</i> api to retrieve the OAuth token.<br />
<br />
It is an alpha version, with a lot of bugs. I need to improve the reading of the mails and the parsing of the gmail URL, as well as the global look. But it is still satisfying.<br />
<br />
Here's a video of the extension in action.<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe src="https://docs.google.com/file/d/0Bx11hauVJpijUThiQktnWUZXX3M/preview" width="640" height="385"></iframe></div>
<br />
And here's the github repo with the source code : <a href="https://github.com/dhatanian/demo-gmail-api">https://github.com/dhatanian/demo-gmail-api</a>Anonymoushttp://www.blogger.com/profile/02803423273909086751noreply@blogger.com0tag:blogger.com,1999:blog-5219665179084602082.post-48340836356797771502014-06-20T20:37:00.000-07:002014-08-06T01:54:26.524-07:00Advanced authentication strategies on OpenAM : choosing the authentication strategy based on client's IP address and the target application<a href="http://forgerock.com/products/open-identity-stack/openam/">OpenAM</a> is a great open source Single Sign On and federation application, with a lot of features accumulated over its years of existence, first as Sun Access Manager, then as Sun and Oracle OpenSSO, before <a href="http://forgerock.com/">Forgerock</a> finally takes over and names it OpenAM.<br />
<br />
Finding the exact feature you want, and working around its limitations, can be quite time consuming.<br />
<br />
In our case, we wanted to be able to choose the right authentication method (say, Kerberos, Active Directory, several types of strong authentication) based on two factors :<br />
<br />
<ul>
<li>IP Address : Is the user coming from the internal network or from the internet ?</li>
<li>Targeted Service Provider : Our OpenAM instance is used as SAML2 IDP, and we want to provide different levels of security depending on which SAML2 Service Provider the user is trying to access. In other words, accessing your Google Apps account should require a stronger authentication than a not-so-secret company-wide intrante.</li>
</ul>
<div>
<br /></div>
<div>
Initially, we only managed IP address-based authentication, for which we used policies as is <a href="http://www.oracle.com/technetwork/java/ipresenvauthopensso2-138348.html">described in several blogs</a>. However, we did not find any easy way to extend this feature to switch the authentication based on the Service Provider.</div>
<div>
<br /></div>
<div>
Here is the solution we eventually implemented :</div>
<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuJzMmK3Cp5w8G5BiTRiPbl9y2cv6UPSF6Xujm1i0nl_XkdPJIv57xBHxrUXJESZ6NjGrYDduQYYYAQgRF3v2mx2h7J-_xk1wET7oAf9muR-uSh9ezhD7CvHlKjZyLpJ2Wn03tY59vym5a/s1600/SSO+IP+++SP+auth+switching.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuJzMmK3Cp5w8G5BiTRiPbl9y2cv6UPSF6Xujm1i0nl_XkdPJIv57xBHxrUXJESZ6NjGrYDduQYYYAQgRF3v2mx2h7J-_xk1wET7oAf9muR-uSh9ezhD7CvHlKjZyLpJ2Wn03tY59vym5a/s1600/SSO+IP+++SP+auth+switching.png" height="480" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
In this architecture, a reverse proxy (we used Apache HTTPd with mod_proxy) redirects the user to one of two realms based on his IP address. Inside each realm, we developped a custom implementation of the IDPAuthnContextMapper class to choose the authentication based on the requested Service Provider.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
</div>
<h3>
IP address-based authentication switching</h3>
<div>
The IDP has two realms "internal" and "external". A reverse proxy is in charge of switching from one realm to the other based on the user's IP address. This way the user only sees one URL, whereas there actually are two :</div>
<div>
<ul>
<li>The SAML endpoint that the user sees : https://sso.example.com/auth/SSORedirect/metaAlias/idp</li>
<li>The actual SAML endpoints that the proxy queries :</li>
<ul>
<li>https://sso.example.com/SSORedirect/metaAlias/internal/idp</li>
<li>https://sso.example.com/SSORedirect/metaAlias/external/idp</li>
</ul>
</ul>
<div>
With this, we can configure completely different authentication modules and chainings depending on where the user comes from.</div>
</div>
<div>
<br /></div>
<div>
This requires a bit of configuration overhead, since me must set the IDP's and SP's configuration twice (once for each realm), but it works well overall.</div>
<div>
<br /></div>
<div>
If you follow OpenAM releases, you may have noticed that there is an <a href="http://openam.forgerock.org/openam-documentation/openam-doc-source/doc/admin-guide/index/chap-auth-services.html#adaptive-auth-module-conf-hints"><i>Adaptative Risk</i> authentication module</a> that can fail or succeed based on a user's IP address. Coupled with authentication chainings, this is another way to perform IP-based authentication switching. However, in our case we wanted to always have Kerberos as the internal authentication method, and with the Adaptative Risk module we could not find a way to avoid showing the Kerberos popup for external users.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKQwoPPUFGFj5szzMID5oI7GkE64XOXV95jP-JdOqNpcWGu_TO6ZCvCu-ywnab79zNwKtn6GtGGtgXCiRIHiQzJJQNiwz2rWquwJ3cf3mTalavXjEPqaMOG3Pblxp9Gl1FoeUnbOIKktEu/s1600/SSO+Kerberos+popup.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKQwoPPUFGFj5szzMID5oI7GkE64XOXV95jP-JdOqNpcWGu_TO6ZCvCu-ywnab79zNwKtn6GtGGtgXCiRIHiQzJJQNiwz2rWquwJ3cf3mTalavXjEPqaMOG3Pblxp9Gl1FoeUnbOIKktEu/s1600/SSO+Kerberos+popup.png" height="300" width="400" /></a></div>
<br />
<br /></div>
<div>
<h3>
Service Provider-based authentication switching</h3>
</div>
<div>
In OpenAM, there is an interface dedicated to choosing the authentication chaining based on the SAML Request, the <a href="https://svn.forgerock.org/openam/trunk/openam/openam-federation/openam-federation-library/src/main/java/com/sun/identity/saml2/plugins/IDPAuthnContextMapper.java">IDPAuthnContextMapper</a>. The default implentation chooses the authentication chaining or module based on the SAML authentication context provided in the SP's SAML request.</div>
<div>
<br /></div>
<div>
In our case, we simply reimplemented the interface to decide based on the issuer provided in the SAML request instead of the authentication context.</div>
<div>
<br /></div>
<div>
We are lucky since OpenAM allows to easily inject your own IPDAuthnContextMapper implementation in replacement of the existing one. Here is what we can see in the IDP configuration screen :</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEih8sm6Gc9eRujILosquRKP1iEKzqKtwmeR1wM1NlNd_TKHB1GNMmySZP9y5kDFomQfPAukqy7lpH5OANXdJa0epjWmSUMxbVR_I7i33Kvtw1ev9TXsxB_14oyU7cpjb_mFdrI4yiex03N0/s1600/2014-06-21_10-35-49.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEih8sm6Gc9eRujILosquRKP1iEKzqKtwmeR1wM1NlNd_TKHB1GNMmySZP9y5kDFomQfPAukqy7lpH5OANXdJa0epjWmSUMxbVR_I7i33Kvtw1ev9TXsxB_14oyU7cpjb_mFdrI4yiex03N0/s1600/2014-06-21_10-35-49.png" height="420" width="640" /></a></div>
<div>
<br /></div>
Anonymoushttp://www.blogger.com/profile/02803423273909086751noreply@blogger.com0tag:blogger.com,1999:blog-5219665179084602082.post-85262586964599042952014-05-06T21:11:00.001-07:002014-05-06T21:12:23.598-07:00Maven, App Engine and YeomanOver the last two years, Javascript's ecosystem of build/scaffodling/dependency management tools has really improved. These days it is quite easy to bootstrap and manage your app using <a href="http://yeoman.io/">Yeoman</a>, for example.<br />
<br />
Now I do use Javascript as the frontend language, but most of the time my backend is written in Java on Google App Engine, and uses good old <a href="http://maven.apache.org/">Maven</a> as the build and dependency management tool.<br />
<br />
Luckily, there is a way to integrate Yeoman into the Maven package, you just have to use the <a href="https://github.com/trecloux/yeoman-maven-plugin">yeoman-maven-plugin</a> by Thomas Recloux. Here is how to integrate it in your pom.xml :<br />
<br />
<pre class="prettyprint linenums"><build>
<plugins>
<plugin>
<groupid>com.github.trecloux</groupid>
<artifactid>yeoman-maven-plugin</artifactid>
<version>0.2</version>
<executions>
<execution>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</pre>
<br />
The next issue is, during development tasks, how do you wire your local development server (for example the App Engine local server or a Jetty instance) so that the "grunt server" command can leverage it ?<br />
<br />
You can do this with the "proxy" feature in Grunt, you will find below what I added to my Gruntfile.js to make it work. <a href="https://github.com/dhatanian/gae-gce-tasks-orchestrator/blob/master/yo/Gruntfile.js">You can also find the full example on Github</a>.
<br />
<br />
The biggest issue I still have with this workflow is that I cannot keep the "grunt server" task running when I package my war, for example to upload on App Engine. Otherwise the Maven execution fails.<br />
<br />
<pre class="prettyprint linenums">connect: {
options: {
port: 9000,
// Change this to '0.0.0.0' to access the server from outside.
hostname: 'localhost',
livereload: 35729
}, proxies: [
{
context: [
'/_ah',
'/admin'
],
host: 'localhost',
port: 8080,
https: false,
changeOrigin: false,
xforward: false
}
], livereload: {
options: {
open: true,
base: [
'.tmp',
'<%= yeoman.app %>'
],
middleware: function (connect, options) {
if (!Array.isArray(options.base)) {
options.base = [options.base];
}
// Setup the proxy
var middlewares = [require('grunt-connect-proxy/lib/utils').proxyRequest];
// Serve static files.
options.base.forEach(function (base) {
middlewares.push(connect.static(base));
});
// Make directory browse-able.
var directory = options.directory || options.base[options.base.length - 1];
middlewares.push(connect.directory(directory));
return middlewares;
}
}
},
...
grunt.loadNpmTasks('grunt-connect-proxy');
...
grunt.registerTask('server', function (target) {
if (target === 'dist') {
return grunt.task.run(['build', 'connect:dist:keepalive']);
}
grunt.task.run([
'clean:server',
'concurrent:server',
'autoprefixer',
'configureProxies:server',
'connect:livereload',
'watch'
]);
});
</pre>
Anonymoushttp://www.blogger.com/profile/02803423273909086751noreply@blogger.com0tag:blogger.com,1999:blog-5219665179084602082.post-86809517136911726372014-05-06T05:05:00.004-07:002014-05-06T05:05:55.659-07:00"Cloud Platform Orchestrator" - An App Engine/Compute Engine demoOne great thing about Compute Engine is how easy it is to manage your resources using the <a href="https://developers.google.com/compute/docs/reference/latest/">API </a>provided by Google.<br />
<br />
I wrote a small Java project to illustrate this. The Compute Engine are launched by App Engine, either on demand or according to a scheduled timing (defined by a Cron Expression).<br />
<br />
The code is here : <a href="https://github.com/dhatanian/gae-gce-tasks-orchestrator">https://github.com/dhatanian/gae-gce-tasks-orchestrator</a><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8T_UwP_8ZwNVkKMGCWb8B-lmDclUsfj5yYRU0D2qVOEs4vQzgK10SQgRsdC1wS9uhmp-IFMO-EoNlSkr6yQ5qL3V8CRqHzVFYbvrmffkv849bQ2jNp_oQIAwJEl5SC7BUPUtP4YxZQ6w5/s1600/createexecution.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8T_UwP_8ZwNVkKMGCWb8B-lmDclUsfj5yYRU0D2qVOEs4vQzgK10SQgRsdC1wS9uhmp-IFMO-EoNlSkr6yQ5qL3V8CRqHzVFYbvrmffkv849bQ2jNp_oQIAwJEl5SC7BUPUtP4YxZQ6w5/s1600/createexecution.png" height="640" width="545" /></a></div>
<br />Anonymoushttp://www.blogger.com/profile/02803423273909086751noreply@blogger.com0tag:blogger.com,1999:blog-5219665179084602082.post-40599023207271139592014-03-11T02:50:00.001-07:002014-03-11T03:01:24.914-07:00Maven : add a custom repository in your source codeYes, yes, you should not do that. It is better to use a custom Maven repository such as Nexus or Archiva.<br />
<br />
However, sometimes there's this library that you want to use in a one-shot project, that you cannot deploy in a custom repository for any reason.<br />
<br />
In that case, you can create a custom maven repository in your source code and add the item that way :<br />
<br />
<pre class="prettyprint linenums">mvn org.apache.maven.plugins:maven-install-plugin:2.5.1:install-file \
-Dfile=path-to-your-artifact-jar \
-DgroupId=your.groupId \
-DartifactId=your-artifactId \
-Dversion=version \
-Dpackaging=jar \
-DlocalRepositoryPath=path-to-specific-local-repo
</pre>
<br />
source : <a href="http://maven.apache.org/plugins/maven-install-plugin/examples/specific-local-repo.html">Maven Install Plugin documentation</a><br />
<br />
In the pom.xml, add the following repository configuration :<br />
<br />
<pre class="prettyprint linenums"><repository>
<id>localRepo</id>
<url>file:${basedir}/repository</url>
</repository>
</pre>
Anonymoushttp://www.blogger.com/profile/02803423273909086751noreply@blogger.com0tag:blogger.com,1999:blog-5219665179084602082.post-28269462722007441622013-12-03T18:39:00.003-08:002013-12-03T18:39:46.729-08:00Yeoman-scaffolded applications and i18next<a href="http://i18next.com/">i18next </a>is a great lib to handle internationalization in javascript. When i first attempted to use it with Yeoman, I had to slightly change the Gruntfile.js to tell it to package the translations in the dist repository.<br />
<br />
You first add the dependency to your project :<br />
<br />
<pre class="prettyprint linenums lang-javascript">bower install i18next --save</pre>
<br />
Then, if you store your translations in the default path (/locales/en/translations.json and so on) then add the following to the Gruntfile.j <i>copy:dist</i> definition :
<br />
<br />
<pre class="prettyprint linenums lang-javascript">copy: {
dist: {
files: [{
expand: true,
dot: true,
cwd: '<%= yeoman.app %>',
dest: '<%= yeoman.dist %>',
src: [
'*.{ico,png,txt}',
'.htaccess',
'bower_components/**/*',
'images/{,*/}*.{gif,webp}',
'fonts/*',
<b>'locales/**/*'</b>
]
}
</pre>
Anonymoushttp://www.blogger.com/profile/02803423273909086751noreply@blogger.com0tag:blogger.com,1999:blog-5219665179084602082.post-80647279214259529162013-12-03T09:18:00.000-08:002013-12-03T09:18:09.964-08:00Yeoman and images handling issues (on Windows 7 64b at least)I'm trying out <a href="https://github.com/yeoman/yeoman">Yeoman</a> these days, along with <a href="http://angularjs.org/">AngularJS</a>, <a href="http://getbootstrap.com/">Bootstrap </a>and the <a href="https://github.com/trecloux/yeoman-maven-plugin">Yeoman Maven plugin</a> to integrate everything in a tool I already know :-)<br />
<br />
The bootstrapping phase works great, even on Windows assuming that you already have a batch like cygwin installed. However I started facing some issues as soon as I added some images in the project.<br />
<br />
After adding some images, I got the following error during <i>grunt build </i>:<br />
<br />
<i> Warning:</i><br />
<i> g:\dev\proj\intranet-homepage\yo\node_modules\grunt-contrib-compass\node_modules\tmp\lib\tmp.js:261</i><br />
<i> throw err;</i><br />
<i> ^</i><br />
<i> Error: spawn ENOENT</i><br />
<i> at errnoException (child_process.js:980:11)</i><br />
<i> at Process.ChildProcess._handle.onexit (child_process.js:771:34) Use --force to continue.</i><br />
<i><br /></i>
<br />
As it turns out, <b>Grunt does not like jpg files.</b> That is, the imagemin module does not. So, converting those jpg files to png did the trick.Anonymoushttp://www.blogger.com/profile/02803423273909086751noreply@blogger.com0tag:blogger.com,1999:blog-5219665179084602082.post-74557327299758928772013-04-04T09:15:00.003-07:002013-04-04T09:15:34.396-07:00Objectify and strange errors with GWT serializations<div style="text-align: justify;">
OK, so it turns out Objectify returns non-serializable lists when using the <i>list()</i> method. Using the following method :</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<br />
<pre style="background-color: white; font-size: 12px; max-width: 80em; padding-left: 0.7em; text-align: start; white-space: pre-wrap;">ofy.load().type(Plant.class).list();
</pre>
<pre style="background-color: white; font-size: 12px; max-width: 80em; padding-left: 0.7em; text-align: start; white-space: pre-wrap;">
</pre>
<pre style="background-color: white; font-size: 12px; max-width: 80em; padding-left: 0.7em; text-align: start; white-space: pre-wrap;">
</pre>
And directly returning the value to GWT client side throws a cryptic serialization exception, where the class that raises the problem is called <i>Proxy$XX </i>where XX are digits.<br />
<br />
That problem is going to be fixed in GWT next version, but meanwhile we have to use the workaround provided in the <a href="https://code.google.com/p/objectify-appengine/issues/detail?id=120">bug report</a>.Anonymoushttp://www.blogger.com/profile/02803423273909086751noreply@blogger.com0tag:blogger.com,1999:blog-5219665179084602082.post-39572531440012636522013-03-24T01:07:00.002-07:002013-03-24T01:26:12.117-07:00App Engine Channel API and Angular JS<div style="text-align: justify;">
I've been using Google App Engine for a while now, and it is hard to go back to traditional java webapp development given how easy it is to deploy a demo version for a customer, and how great some of the tools provided in the App Engine SDK are.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Among those tools is the <a href="https://developers.google.com/appengine/docs/java/channel/">Channel API</a>. It offers push messaging with only a few lines of code. There are some drawbacks (mainly regarding the reliability of the communications) but it still is a <b>great</b> tool to provide real-time notifications to the user.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
A also recently discovered <a href="http://angularjs.org/">Angular JS</a>. A great Javascript MVP framework (also by Google, it turns out). If you don't know it already, have a look at their web site. The web-binding part is quite amazing.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Now, I read Angular JS provides some support for Comet notifications, but nothing for the Channel API. On a project I'm developping on my spare time there's this need to send chat messages to various users.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
</div>
<a name='more'></a><br />
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Here's how I did it with AngularJS and the Channel API :</div>
<div style="text-align: justify;">
</div>
<ul>
<li>Create an AngularJS service to hold the list of the chats. This is not required but it is the recommended way to share an observable array (which means with two-way data binding) between controllers.</li>
<li>Initiate the Channel API inside that service. Now there's the trick : for the modifications to the observable array to be pushed to the controllers, we need to use the <i>$rootScope </i>method.</li>
</ul>
<div>
So here's how the service looks ( the chat repository which receives the chats from the Channel API is <i>AllChats</i>):</div>
<div>
<br /></div>
<pre class="prettyprint linenums lang-java">
angular.module('hatChatServices', ['ngResource'])
.factory('Chat', function($resource){
return $resource('chats/:chatId', {}, {
query: {method:'GET', params:{chatId:'latest'}, isArray:true},
});
})
.factory('AllChats', function($rootScope,Chat){
var chatsList = Chat.query();
var unreadChats = {
"PENDING" : 0,
"APPROVED" : 0,
"ANSWERED" : 0
}
//that's where we connect
var socket = new SocketHandler();
socket.onMessage(function(data){
$rootScope.$apply(function () {
var newChat = new Chat(data);
angular.copy(removeChatIfAlreadyExists(newChat, chatsList), chatsList);
chatsList.push(newChat);
unreadChats[newChat.type]++;
});
});
return {
list : chatsList,
unreadChats : unreadChats
}
});
function removeChatIfAlreadyExists(chat, array){
var result = array.filter(function(potentialMatch){
return potentialMatch.id != chat.id;
})
return result;
}
</pre>
<br />
<br />
And here's the definition of the <i>SocketHandler </i>:<br />
<div>
<br /></div>
<pre class="prettyprint linenums lang-java">
var SocketHandler = function(){
this.messageCallback = function(){};
this.onMessage = function(callback){
var theCallback = function(message){
callback(JSON.parse(message.data));
}
if(this.channelSocket ==undefined){
this.messageCallback = theCallback;
}else{
this.channelSocket.onmessage = theCallback;
}
}
var context = this;
this.socketCreationCallback = function(channelData){
var channel = new goog.appengine.Channel(channelData.channelToken);
context.channelId = channelData.channelId;
var socket = channel.open();
socket.onerror = function(){
console.log("Channel error");
};
socket.onclose = function(){
console.log("Channel closed, reopening");
//We reopen the channel
context.messageCallback = context.channelSocket.onmessage;
context.channelSocket = undefined;
$.getJSON("chats/channel",context.socketCreationCallback);
};
context.channelSocket = socket;
console.log("Channel info received");
console.log(channelData.channelId);
context.channelSocket.onmessage = context.messageCallback;
};
$.getJSON("chats/channel",this.socketCreationCallback);
}
</pre>Anonymoushttp://www.blogger.com/profile/02803423273909086751noreply@blogger.com0