=pod =head1 OAuth, RESTy APIs, Microservices, ... =head2 META =head3 Author Thomas Klausner =head3 Email domm AT plix.at =head3 URL http://domm.plix.at =head3 URL_slides http://domm.plix.at/talks/wien_oauth =head3 Date 2015-04-22 =head3 Location sector5 =head3 Event Joined Meetup =head2 /me Thomas Klausner domm http://domm.plix.at @domm_favors_irc =for newslide =for img vienna.pm.org.png =for newslide =for img bicycle.pm.png =for newslide =for img validad.com.png =for newslide http://oe1.orf.at Die Angewandte ... =head2 Microservices Instead of one big monolithic application use several distinct microservices that do one thing and do it well =head2 RESTy APIs We're still talking HTTP But instead of producing one big chunk of HTML the backends spit out JSON And the frontend renders the JSON Frontend calls backend APIs using HTTP verbs =for newslide B (I'm very happy about that...) =for newslide People like to argue about what REST is. Content Negotiation, HATEOAS, HTTP Verbs, and 100 more bikesheds I don't care (a lot), I just want to get things done.. RESTB =head2 OAuth2 OAuth2 allows you to give an application some rights to use another application on your behalf. This is very handy for microservices! It can also be used (or abused) as an authentication system ("OpenID Connect") Most people only think of this when we're talking about OAuth2 =head3 Authentication via OAuth2 Log in with Google / Facebook / big-fat-service.com =for newslide UserAgent your-site.com google.com =for newslide UserAgent your-site.com google.com User gets session-id =for newslide UserAgent your-site.com google.com User gets session-id < LOG IN WITH GOOGLE > =for newslide UserAgent your-site.com google.com User gets session-id < LOG IN WITH GOOGLE > Redirect to google.com/o/oauth/$your_client_id =for newslide UserAgent your-site.com google.com User gets session-id < LOG IN WITH GOOGLE > Redirect to google.com/o/oauth/$your_client_id < Enter Password > =for newslide UserAgent your-site.com google.com User gets session-id < LOG IN WITH GOOGLE > Redirect to google.com/o/oauth/$your_client_id < Enter Password > OK! Redirect to your-site.com/postback?code=123xyz =for newslide UserAgent your-site.com google.com User gets session-id < LOG IN WITH GOOGLE > Redirect to google.com/o/oauth/$your_client_id < Enter Password > OK! Redirect to your-site.com/postback?code=123xyz Take the code post it to google with secret =for newslide UserAgent your-site.com google.com User gets session-id < LOG IN WITH GOOGLE > Redirect to google.com/o/oauth/$your_client_id < Enter Password > OK! Redirect to your-site.com/postback?code=123xyz Take the code post it to google with secret Verify secret Return AccessToken =for newslide UserAgent your-site.com google.com User gets session-id < LOG IN WITH GOOGLE > Redirect to google.com/o/oauth/$your_client_id < Enter Password > OK! Redirect to your-site.com/postback?code=123xyz Take the code post it to google with secret Verify secret Return AccessToken Mark user logged in Store AccessToken =for newslide UserAgent your-site.com google.com User gets session-id < LOG IN WITH GOOGLE > Redirect to google.com/o/oauth/$your_client_id < Enter Password > OK! Redirect to your-site.com/postback?code=123xyz Take the code post it to google with secret Verify secret Return AccessToken Mark user logged in Store AccessToken Show some content =for newslide You can watch a free dance performance of this process here: https://www.youtube.com/watch?v=xeGxGnSkSdQ YAPC::Europe 2014 Lightning Talks: OAuth 2.0 Authorization Explained =head4 Results User is logged in at big-fat-service.com (most likely was already logged in) User is logged in at your-site.com without needing a password for your-site.com You have an Access Token for big-fat-service.com =head4 Side note - Flows I showed you the "Web Server Flow". There are a few more flows, the most interesting being the "Popup Flow" You can use this in JavaScript to Login and get an Access Token. You can than store the token in local storage and/or send it to your backend =head3 Another side note - Grants Usually your-site.com asks for a set of permissions to be used on big-fat-service.com Login, Post to Wall, Read List of Friends During the login process, the user is asked if he wants to grant this set of permissions =for newslide =for img grants.png =head3 Using the Access Token B i.e. User does something on your-site.com, and you immediatly post something to facebook B i.e. at midnight, post a summary of users actions to facebook =head4 Examples GET https://www.googleapis.com/plus/v1/people/me/people/visible?key={YOUR_API_KEY} { "kind": "plus#peopleFeed", "etag": "\"RqKWnRU4WW46-6W3rWhLR9iFZQM/ObDqMibaPxXUs7wb-vH72G2zQbI\"", "title": "Google+ List of Visible People", "totalItems": 0, "items": [ ] } =for newslide Or: GET https://content.googleapis.com/plus/v1/people/me Authorization: Bearer AIzaSyCFj15TpkchL4OUhLD1Q2zgxQnMb7v3fas Bearer Token! We'll take about those in a minute.. =for newslide Only use tokens over SSL A Token is just a fancy password A Token is just a fancy session cookie or is it? =head2 JWT pronounced "jot" - JSON Web Token eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJqb2UiLCJleHAiOjEzMDA4MTkzODAsImlzX3Jvb3QiOjF9.ToqKERcPNY1euV-jwpQunfmuN1H3Sj3hRo82MXXF5rw eyJhbGciOiJIUzI1NiJ9. eyJzdWIiOiJqb2UiLCJleHAiOjEzMDA4MTkzODAsImlzX3Jvb3QiOjF9. ToqKERcPNY1euV-jwpQunfmuN1H3Sj3hRo82MXXF5rw Three parts seperated by dot, each part base64url encoded =for newslide Header, Body ("Claims"), Signature Headers tells us how the Claims are encrypted / signed Body is a hash of so-called claims (e.g. user-id, expiry date, issuer, ..) Signature is a signature =for newslide eyJhbGciOiJIUzI1NiJ9. eyJzdWIiOiJqb2UiLCJleHAiOjEzMDA4MTkzODAsImlzX3Jvb3QiOjF9. ToqKERcPNY1euV-jwpQunfmuN1H3Sj3hRo82MXXF5rw =for newslide {"alg":"HS256"} eyJzdWIiOiJqb2UiLCJleHAiOjEzMDA4MTkzODAsImlzX3Jvb3QiOjF9. ToqKERcPNY1euV-jwpQunfmuN1H3Sj3hRo82MXXF5rw =for newslide {"alg":"HS256"} {"exp":1300819380,"is_root":1,"iss":"joe"} ToqKERcPNY1euV-jwpQunfmuN1H3Sj3hRo82MXXF5rw =for newslide {"alg":"HS256"} {"exp":1300819380,"is_root":1,"iss":"joe"} signature based on alg & content =for newslide Or you use a library to verify & unpack it: use Data::Dumper; use JSON::WebToken; my $claims = JSON::WebToken->decode( $token, $secret ); say Dumper $claims; { 'exp' => 1300819380, 'is_root' => 1, 'sub' => 'joe' }; =head3 Claims There are a few official claims, but you can allways add more private claims. =over =item * B = Subject of the token, mostly the user-id / email =item * B = Issuer: who issued the token (eg google.com) =item * B = Audience: whom the token is intended to (eg your service-id) =item * B = Expiration Time =item * and some more timestamps related claims =back =for newslide JWTs are cool Tokens because the can be signed & even encrypted If you are a API and receive a JWT, you can just verify the signature, look at the claims, and do whatever you (or the caller) wants to do. For true stateless backends! =head2 Semi-live Demo =head3 Authorize We wrote our own OAuth2 server which might be a bit of an overkill, but was a lot of fun and a good way to really understand the protocol I'll log in there =for newslide =for img accounts_login.png =for newslide =for img accounts_logged_in.png =head3 Getting a token Now that I'm logged in, I can get a Token =for newslide =for img accounts_issue_token.png =for newslide eyJhbGciOiJIUzI1NiJ9. eyJzdWIiOiJiODA0NGY0MC05ZjQ2LTQxOWUtYWJiYi0zOWY4YjkwY2VhZDMiLCJ leHAiOjE0Mjk3NDkxODIsImlhdCI6MTQyOTcyMDM4MiwiaXNzIjoiYWNjb3VudH MudmFsaWRhZCIsImF1ZCI6IjE0MjBkMjMwLWE0ZTMtNGQyYS1iMGEwLTQ0N2RhZ TUxMTVlZiJ9. ntOgffomGiFC0LlL-5q-TJCCjIlrsMopI2yTxLO1qZk =for newslide perl -Mlocal::lib=local -MJSON::WebToken -MData::Dumper -E ' my $d = JSON::WebToken->decode("eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJiODA0NGY0MC05ZjQ2LTQxOWUtYWJiYi0zOWY4YjkwY2VhZDMiLCJleHAiOjE0Mjk3NDkxODIsImlhdCI6MTQyOTcyMDM4MiwiaXNzIjoiYWNjb3VudHMudmFsaWRhZCIsImF1ZCI6IjE0MjBkMjMwLWE0ZTMtNGQyYS1iMGEwLTQ0N2RhZTUxMTVlZiJ9.ntOgffomGiFC0LlL-5q-TJCCjIlrsMopI2yTxLO1qZk","*******"); say Dumper $d' =for newslide { 'exp' => 1429749182, 'sub' => 'b8044f40-9f46-419e-abbb-39f8b90cead3', 'aud' => '1420d230-a4e3-4d2a-b0a0-447dae5115ef', 'iat' => 1429720382, 'iss' => 'accounts.validad' } =for newslide Now we can use that token everywhere to use the API I could fax the token to you and you could use it =head3 Using the token I'll use a very simple Comment API we've developed It does comments, but you can also rate arbitrary stuff =for newslide Let's just call the API and GET some public data curl http://localhost:5300/api/604a8c70-4bb4-4658-b2e8-7bd37054fc1b\ /ratings/stars/sector5 =for newslide { "count":"0", "avg":null } =for newslide Now let's PUT to add a rating curl http://localhost:5300/api/604a8c70-4bb4-4658-b2e8-7bd37054fc1b\ /ratings/stars/sector5 -XPUT -d '{ "rating" : 5 }' =for newslide Error 401

Error 401

401 Unauthorized

=for newslide curl http://localhost:5300/api/604a8c70-4bb4-4658-b2e8-7bd37054fc1b\ -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJiODA0N...\ /ratings/stars/sector5 -XPUT -d '{ "rating" : 5 }' =for newslide { "status": "AdddedRating", "ratings": { "avg": "5.0000000000000000", "count":"1" } } Yay! =head2 Key point Whoever has the token, can act as the user You can pass the token to the JS frontend and directly call any backend that accepts the token (you maybe have to fight with CORS, but that's still easier than fighting against cookies AND CORS) You can store the token in some backend and have it call another backend You can store the token in a commandline script / desktop app I assume you can do stuff with mobile apps, too. =head2 Shut the fuck up and show some code ?? =head2 Questions / Discussion