Upload
atlassian
View
402
Download
2
Embed Size (px)
DESCRIPTION
Atlassian provides two easy-to-use frameworks for getting a Connect add-on up and running quickly – atlassian-connect-express and ac-play. But what if these frameworks don't quite fit your bill? What does it mean to build a Connect add-on with your own stack? What components do you need to write? And how does it all fit together? Attending this talk will give you enough background information to implement an add-on in the language and technology stack of your choice.
Citation preview
June 3-5, 2014 | Berlin, Germany
Patrick Streule, Architect, Atlassian
Build a Connect Add-on with Your Own Stack
• Atlassian Connect Express: node.js/express
• Atlassian Connect Play: Java/Play
• ACE and AC-Play provide:
• Authentication handling (both directions)
• Lifecycle handling
• Persistence
ACE/AC-Play vs Your Own
• You have an existing service that will host the add-on
• Development in your company happens on a different stack
• Connect does not prescribe a stack at all
ACE/AC-Play vs Your Own
Overview
Product
Overview
WEBHOOKS
REST API
REST API
HTTP
Add-On
Serve descriptor and add-on UI ☑ Web server / web framework ☑ SSL certificate
Handle add-on installation ☑ Persistent store
Handle add-on requests ☑ JWT token handler ☑ Crypto library ☑ JSON and HttpClient libs
Checklist
The Minimum Viable Add-On
• Description of the Add-On
• Where does it show up?
• Where are the add-on endpoints?
• General Metadata
The Descriptor{!
"key": "atlas-camp",!
"name": "AtlasCamp",!
"baseUrl": “https://addon.example.com”,!
"authentication": {!
"type": "none"!
},!
"modules": {!
"generalPages": [!
{!
"url": "/intro.html",!
"key": "intro-page",!
"name": {!
"value": "Atlas Camp"!
}!
}!
]!
}!
}
The Content<!DOCTYPE html>!
<html>!
<head>!
<title>Atlas Camp</title>!
<script src=“https://someinstance.jira.com/atlassian-connect/all.js”></script>!
! <style>body { text-align: center; padding-top: 50px; }</style>!
</head>!
<body>!
<div class=“ac-content”>!
<a href=“https://www.atlassian.com/atlascamp/2014">!
! <img src=“img/atlas-camp.png"/>!
</a>!
</div>!
</body>!
</html>
The Result
• HTTPS in production, HTTP for development only
• Include all.js from the host:
The Important Bits
var hostBaseUrl = getUrlParameter("xdm_e");!
var contextPath = getUrlParameter("cp");!
loadScript(hostBaseUrl + contextPath + "/atlassian-connect/all.js");
Adding Authentication: Installation Event
• Authentication type: JWT
• Installed Lifecycle hook is required.
• Will be called during installation
• Other hooks:
• Uninstalled, Enabled, Disabled
!
Setting it up{!
"key": "atlas-camp",!
"name": "AtlasCamp",!
"baseUrl": “https://addon.example.com”,!
"authentication": {!
"type": "jwt"!
},!
"lifecycle": {!
"installed": "/installed",!
"uninstalled": "/uninstalled"!
}!
}
Installation event• clientKey
Identifies the tenant
• sharedSecretThe key for signing and verifying JWT tokens
• baseUrlThe host and context path of the product
Adding Authentication: JWT
• Authenticity
• Parties are who they claim to be
• Integrity
• The request was not tampered with
• Authorization (special case in Connect)
• User has access to the pages and issues referenced by URL parameters
JWT
JWT: Anatomy of a token
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0MDEwODEwMzgsInN1YiI6ImZmODA4MTgxNDBjMDcyOWEwMTQwY2Q0NWE5MWQwMDBkIiwiaXNzIjoiQ29uZmx1ZW5jZTowNzA3MjM3MjM2IiwicXNoIjoiMjU5YzZkNjU1NjEwYzgyNzE3MDMxNWEwMTM1ZGI0OTAwODYxZjkxYzA5NDdlM2I2NjY2NjgyZTkzMDU1NWFiNCIsImlhdCI6MTQwMTA4MDg1OH0.iSmtl3ukm8EohrCwO94MF7sXeEFtIRQ-aBggghjlE0E
JWT: Anatomy of a token
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0MDEwODEwMzgsInN1YiI6ImZmODA4MTgxNDBjMDcyOWEwMTQwY2Q0NWE5MWQwMDBkIiwiaXNzIjoiQ29uZmx1ZW5jZTowNzA3MjM3MjM2IiwicXNoIjoiMjU5YzZkNjU1NjEwYzgyNzE3MDMxNWEwMTM1ZGI0OTAwODYxZjkxYzA5NDdlM2I2NjY2NjgyZTkzMDU1NWFiNCIsImlhdCI6MTQwMTA4MDg1OH0.iSmtl3ukm8EohrCwO94MF7sXeEFtIRQ-aBggghjlE0E
Header
Payload, Claims
Signature
JWT: Anatomy of a token
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0MDEwODEwMzgsInN1YiI6ImZmODA4MTgxNDBjMDcyOWEwMTQwY2Q0NWE5MWQwMDBkIiwiaXNzIjoiQ29uZmx1ZW5jZTowNzA3MjM3MjM2IiwicXNoIjoiMjU5YzZkNjU1NjEwYzgyNzE3MDMxNWEwMTM1ZGI0OTAwODYxZjkxYzA5NDdlM2I2NjY2NjgyZTkzMDU1NWFiNCIsImlhdCI6MTQwMTA4MDg1OH0.iSmtl3ukm8EohrCwO94MF7sXeEFtIRQ-aBggghjlE0E
{"alg":"HS256","typ":"JWT"}
{! "exp": 1401081038,! "sub": “ff808…d000d”,! "iss": "Confluence:070789",! "qsh": “259c6…55ab4”,! "iat": 1401080858!}
HMAC using SHA-256
Expires at
Subject
Issuer
Query String Hash Issued
at
BTW: Never rely on this prefix!
JWT Verification: Step 1base64url(sign(‘eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0MDEwODEwMzgsInN1YiI6ImZmODA4MTgxNDBjMDcyOWEwMTQwY2Q0NWE5MWQwMDBkIiwiaXNzIjoiQ29uZmx1ZW5jZTowNzA3MjM3MjM2IiwicXNoIjoiMjU5YzZkNjU1NjEwYzgyNzE3MDMxNWEwMTM1ZGI0OTAwODYxZjkxYzA5NDdlM2I2NjY2NjgyZTkzMDU1NWFiNCIsImlhdCI6MTQwMTA4MDg1OH0’, header.alg, tenants[claims.iss].sharedSecret))!!== iSmtl3ukm8EohrCwO94MF7sXeEFtIRQ-aBggghjlE0E ?
JWT Verification: Step 2
a=5,7&lic=none&page.id=19809&user_id=pstreule
{! "baseUrl": “https://addon.example.com/base”,…!}
GET /base/render?page.id=19809&a=7&a=5&user_id=pstreule&lic=none&jwt=eY…
"qsh": “259c6…55ab4”
hex(sha256(‘GET&…’))
remove context path remove ‘jwt’, order keys and values, apply OAuth1 encoding rulesuppercase
/renderGET &&
• Characters in the unreserved character set MUST NOT be encoded: (ALPHA, DIGIT, "-", ".", "_", "~")
• All other characters MUST be encoded
• The two hexadecimal characters used to represent encoded characters MUST be uppercase.
Pitfalls
Raw Encoded
SPACE %20
* %2A
! %21
' %27
( %28
) %29
• JWT tokens used on incoming and outgoing requests
• URL Parameter: jwt=eY…
• HTTP header: Authorization: JWT eY…
• Find more information
• http://go.atlassian.com/ac-docs
• http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html
JWT Summary
Adding Authentication: Add-On to Add-On
• Do not use session cookies
• Cookies set by the iframe are third-party cookies
• Many browsers don’t accept them by default
Pitfalls
• How to handle authentication for intra-add-on Ajax calls and links.
Your own requests
• Create a JWT token for the tenant and user
• QSH is optional, depending on the parameter data
Reuse JWT
{! "exp": 1401081038,! "sub": “ff808…d000d”,! "iss": "Confluence:070789",! "iat": 1401080858!}
Dev Speed
Quick descriptor deploymentcurl -i -X HEAD -u admin:admin http://localhost:2990/jira/rest/plugins/1.0/
HTTP/1.1 200 OK!upm-token: 7864481825707347853
curl -i -X POST -u admin:admin ! -H "Content-type: application/vnd.atl.plugins.remote.install+json" -d '{"pluginUri":"http://localhost:3000/atlassian-connect.json"}'! http://localhost:2990/jira/rest/plugins/1.0/?token=7864481825707347853
We’re here to helphttp://go.atlassian.com/ac-dev