I think Jesse hinted at this earlier, but it looks like the issues you described are more related to the auth tokens than the 3-legged Oauth.
https://issues.apache.org/jira/browse/RAVE-20 https://issues.apache.org/jira/browse/RAVE-82 I *think* that the auth token is not required for Oauth to work, though it is absolutely necessary in the real world; however, given that they can be worked on separately, I recommend taking one problem on at a time :) -Matt On 6/28/11 6:19 AM, "Jasha Joachimsthal" <[email protected]> wrote: >Hi all, > >to give an update on this issue: we're still stuck but got to know the >internals of Shindig a bit better. > >The key file was indeed incorrect, thanks Jesse for the script to generate >the key. Should we document this script or add it to the code base? > >CommonContainerSecurityTokenCodec does use BlobCrypterSecurityTokenCodec >by >default but its createToken method returns TestSecurityTokenCodec while >DefaultContainerSecurityCodes#createToken returns a >BlobCrypterSecurityToken. > >We've added a shindig.auth.updateSecurityToken('${securityToken}'); before >rendering the widgets and this method seems to work but not for the actual >rendering of the gadgets. > >When in rave_opensocialapi.js calls container.navigateGadget(gadgetSite, >gadget.widgetUrl, {}, renderParams); we get an iframeUrl with security >token >%st% back. This is a result >of DefaultIframeUriManager#generateSecurityToken. It tries to encode an >AnonymousSecurityToken in the BlobCrypterSecurityTokenCodec class which >fails. >We can block the AnonymousSecurityToken by overriding SocialApiGuiceModel >but then the gadget is not rendered at all because the metadata call in >OpenSocialWidgetRenderer fails. > >We're lost now in the magic that happens inside container.navigateGadget >so >I've just posted a question on the Shindig-dev list how we can get an >iframeUrl with a valid security token. > >Regards, > >Jasha Joachimsthal > >Europe - Amsterdam - Oosteinde 11, 1017 WT Amsterdam - +31(0)20 522 4466 >US - Boston - 1 Broadway, Cambridge, MA 02142 - +1 877 414 4776 (toll >free) > >www.onehippo.com > > >On 23 June 2011 17:34, Okke Harsta <[email protected]> wrote: > >> Hi Jesse, >> >> Thanks for comments. We are trying to implement it step-by-step, but >>things >> are kind of related in order to be able to install a gadget that does a >> three-legged call to Google to retrieve all of your contacts. Basically >>the >> missing piece of information is how we can get the client side (= rave >> server side) generated securityToken into the osapi call (for now >>Shindig >> produces an error when encountering the default token %st%). >> >> In the SURFconext codebase we did this by putting the securityToken on >>the >> gadget in JavaScript and calling shindig.container.addGadget: >> >> gadget.secureToken = COIN.securityTokens[i]; //generated by the Portal >> server >> gadget.userPrefs = shindig.container.userPrefStore.getPrefs(gadget); >> shindig.container.addGadget(gadget); >> } >> shindig.container.layoutManager.setGadgetChromeIds(gadgetIds); >> shindig.container.renderGadgets(); >> >> However with the new common container ( >> http://comments.gmane.org/gmane.comp.web.shindig.devel/6286) this seems >>to >> have changed. >> >> You're right about the pem file, but that is a naming miscommunication: >> gadgets.securityTokenKeyFile does point to our key file which we also >>use to >> create the token on the Rave server: >> >> BasicBlobCrypter blobCrypter = new BasicBlobCrypter(key.getBytes()); >> BlobCrypterSecurityToken st = new BlobCrypterSecurityToken(blobCrypter, >> environment.getContainerName(), "localhost"); >> st.setViewerId(personId); >> st.setOwnerId(personId); >> st.setAppUrl(gadget.getDefinition().getUrl()); >> String token = Utf8UrlCoder.encode(st.encrypt()); >> >> To be continued... >> >> Okke >> >> On Jun 23, 2011, at 4:47 PM, Ciancetta, Jesse E. wrote: >> >> > Hi Jasha, >> > >> > Great to hear that you and Okke are taking this on -- it will be >>awesome >> to get security tokens and three-legged OAuth working. >> > >> > One thing that might help in general is to just try to do one piece of >> this at a time -- it sounds like your trying to get security tokens, >> three-legged OAuth and alternate config location loading all working at >>the >> same time which seems like it could be a challenge... It would also >> probably be good to commit them one piece at a time too so it will be >>easier >> for us to understand what the changes do (which you may have been >>planning >> to do already). >> > >> > When I ran through this when working on OSEC internally I ran into >>issues >> with getting all the right keys setup in the right format and in the >>right >> place. I ended up creating a script to create everything which I >>thought >> might be helpful (I did some search/replace on paths and filenames so >> hopefully I didn't break anything): >> > >> > ----------- BEGIN SCRIPT ----------- >> > >> > # local variables >> > TMP_DIR=/tmp/shindig_keys >> > PRIVATE_KEYS_DIR=/private/shindig/keys >> > PUBLIC_KEYS_DIR=/public/shindig/keys >> > >> > # make a temporary working directory >> > mkdir $TMP_DIR >> > cd $TMP_DIR >> > >> > # generate our private key -- this is used by shindig to sign requests >> > openssl genrsa -out portal_signing_key_private.pem 1024 >> > >> > # create a pkcs8 formatted version of the private key -- this seems >>to be >> the format shindig wants the key in -- this is the filed used for >> shindig.signing.key-file in shindig.properties >> > openssl pkcs8 -in portal_signing_key_private.pem -out >> portal_signing_key_private.pk8.pem -topk8 -nocrypt -outform PEM >> > >> > # create a DER formatted version of the private key in case we need >>it at >> some point in the future >> > openssl rsa -in portal_signing_key_private.pem -inform PEM -out >> portal_signing_key_private.der -outform DER >> > >> > # export the public key component in PEM format - this could be used >>by >> third parties to validate the signature on shindig signed requests >> > openssl rsa -in portal_signing_key_private.pem -pubout -out >> portal_signing_key_public.pem >> > >> > # export the public key component in DER format - this could be used >>by >> third parties to validate the signature on shindig signed requests >> > openssl rsa -in portal_signing_key_private.pem -pubout -out >> portal_signing_key_public.der -outform DER >> > >> > # create a new x509 certificate based on our private key and write it >>out >> in PEM format - this could be used by third parties to validate the >> signature on shindig signed requests >> > openssl req -new -x509 -key portal_signing_key_private.pem -days 365 >> -subj '/C=US/ST=MA/L=Bedford/O=Foo Corp/OU=Foo/CN=foo.example.com' -out >> portal_signing_certificate.pem >> > >> > # create a DER formatted version of our x509 certificate - this could >>be >> used by third parties to validate the signature on shindig signed >>requests >> > openssl x509 -in portal_signing_certificate.pem -inform PEM -out >> portal_signing_certificate.der -outform DER >> > >> > # generate the shared secret symmetric key used by shindig and the >> container to encrypt/decrypt gadget security tokens - this is the file >>used >> for gadgets.securityTokenKeyFile in container.js >> > dd if=/dev/random bs=32 count=1 | openssl base64 > >> portal_security_token_key_private.txt >> > >> > # copy the private keys to the shindig directory >> > mkdir $PRIVATE_KEYS_DIR >> > cp portal_signing_key_private.* portal_security_token_key_private.txt >> $PRIVATE_KEYS_DIR >> > >> > # copy the public keys and certificates to the public keys directory >> > mkdir $PUBLIC_KEYS_DIR >> > cp portal_signing_key_public.* portal_signing_certificate.* >> $PUBLIC_KEYS_DIR >> > >> > # remove the temporary working directory >> > cd .. >> > rm -rf $TMP_DIR >> > >> > ----------- END SCRIPT ----------- >> > >> > Additional comments inline below. >> > >> >> -----Original Message----- >> >> From: Jasha Joachimsthal [mailto:[email protected]] >> >> Sent: Thursday, June 23, 2011 8:37 AM >> >> To: [email protected] >> >> Subject: 3-legged oAuth >> >> >> >> Hi, >> >> >> >> Okke and I are trying to get 3 legged oAuth working in Rave but we're >> stuck >> >> on passing the security token from the portal to the (Shindig) >> container. >> >> >> >> What we have done so far: >> >> - generated a key using >> >> openssl req -newkey rsa:1024 -days 365 -nodes -x509 -keyout >>testkey.pem >> - >> >> out >> >> testkey.pem -subj '/CN=mytestkey' >> >> openssl pkcs8 -in testkey.pem -out oauthkey.pem -topk8 -nocrypt >>-outform >> >> PEM >> >> >> >> - replaced the default PropertiesModule by a properties module that >>can >> read >> >> shindig properties from a different location >> >> - configured our custom shindig properties to read a different >> container.js >> >> in shindig.containers.default >> >> >> >> In our custom container.js we've set >> >> "gadgets.securityTokenType" : "secure", >> >> "gadgets.securityTokenKeyFile" : "/path/to/my/oauthkey.pem", #with >>the >> >> correct location of course >> > >> > This doesn't look like the right file to me -- this looks like what >> shindig might use to sign outbound requests, not what's used for >> encrypting/decrypting security tokens. Using the script I posted above >>I >> think the path you'd want would be: >> > >> > /private/shindig/keys/portal_security_token_key_private.txt >> > >> >> >> >> - replaced CommonContainerAuthGuiceModule with a custom module that >> >> loads DefaultSecurityTokenCodec instad of >> >> CommonContainerSecurityTokenCodec >> > >> > It doesn't seem like you should need to do this -- the >> CommonContainerSecurityTokenCodec seems to use the >> BlobCrypterSecurityTokenCodec by default when it encounters the "secure" >> configuration in container.js which seems like what we want. >> > >> >> - added a gadget from http://gadgets.jasha.eu/oauthtest.xml (and >> registered >> >> the domain in Google) >> >> - added consumer key & secret to config/oauth.json >> >> >> >> Now when I add this gadget to my page, I get a HTTP Status 401 - >> Malformed >> >> security token %st% Invalid security token %st% response. >> >> This makes sense because the portal doesn't pass a security token to >> >> Shindig. This could be done by generating a token like in [1], in >>Rave >> >> probably in OpenSocialWidgetRenderer, and then set the security >>token to >> > >> > Agreed -- OpenSocialWidgetRenderer does seem like the right place to >>do >> this but I think I might push the actual work off to a >>SecurityTokenService >> which takes the RegionWidget and returns the SecurityToken. We've >>updated >> our SecurityTokenService internally and haven't pushed those updates >>back to >> the public OSEC codebase yet, so I'll post what we have currently here >>in >> hopes that it might be useful (the changes from what's in public OSEC >> currently is basically that this service no longer does any URL >>encoding of >> the values it deals with since this really isn't the right architectural >> layer to be doing that work): >> > >> > ---------- >> > >> > import org.apache.shindig.auth.SecurityToken; >> > import org.mitre.portal.model.PersonGadget; >> > import org.mitre.portal.service.exception.SecurityTokenException; >> > >> > public interface SecurityTokenService { >> > SecurityToken getSecurityToken(PersonGadget personGadget) throws >> SecurityTokenException; >> > String getEncryptedSecurityToken(PersonGadget personGadget) throws >> SecurityTokenException; >> > SecurityToken decryptSecurityToken(String encryptedSecurityToken) >> throws SecurityTokenException; >> > String refreshEncryptedSecurityToken(String encryptedSecurityToken) >> throws SecurityTokenException; >> > } >> > >> > ---------- >> > >> > import org.apache.commons.logging.Log; >> > import org.apache.commons.logging.LogFactory; >> > import org.apache.shindig.auth.BlobCrypterSecurityToken; >> > import org.apache.shindig.auth.MitreBlobCrypterSecurityToken; >> > import org.apache.shindig.auth.SecurityToken; >> > import org.apache.shindig.common.crypto.BasicBlobCrypter; >> > import org.apache.shindig.common.crypto.BlobCrypter; >> > import org.apache.shindig.common.util.CharsetUtil; >> > import org.mitre.portal.model.Gadget; >> > import org.mitre.portal.model.Person; >> > import org.mitre.portal.model.PersonGadget; >> > import org.mitre.portal.security.SecurityTokenService; >> > import org.mitre.portal.security.UserService; >> > import org.mitre.portal.service.exception.SecurityTokenException; >> > import org.springframework.beans.factory.annotation.Autowired; >> > import org.springframework.security.access.prepost.PreAuthorize; >> > import org.springframework.stereotype.Service; >> > >> > import java.io.File; >> > import java.io.IOException; >> > import java.net.MalformedURLException; >> > import java.net.URL; >> > import java.util.ResourceBundle; >> > >> > @Service(value = "securityTokenService") >> > public class EncryptedBlobSecurityTokenService implements >> SecurityTokenService { >> > private static final Log log = >> LogFactory.getLog(EncryptedBlobSecurityTokenService.class); >> > >> > private static final String SECURITY_PROPERTIES = "application"; >> > private static final String ENCRYPTION_PROPERTY = >> "security.encryptionkey"; >> > private static final String EMBEDDED_KEY_PREFIX = "embedded:"; >> > //TODO - should this be read in from a properties file? >> > private static final String CONTAINER = "default"; >> > private static final String DOMAIN = " default"; >> > private static final String CHAR_ENCODING = "UTF-8"; >> > >> > private final UserService userService; >> > private final BlobCrypter blobCrypter; >> > >> > @Autowired >> > public EncryptedBlobSecurityTokenService(UserService userService) { >> > this.userService = userService; >> > >> > ResourceBundle resourceBundle = >> ResourceBundle.getBundle(SECURITY_PROPERTIES); >> > if (resourceBundle.containsKey(ENCRYPTION_PROPERTY)) { >> > String key = resourceBundle.getString(ENCRYPTION_PROPERTY); >> > if (key.startsWith(EMBEDDED_KEY_PREFIX)) { >> > byte[] encryptionKey = >> CharsetUtil.getUtf8Bytes(key.substring(EMBEDDED_KEY_PREFIX.length())); >> > this.blobCrypter = new BasicBlobCrypter(encryptionKey); >> > } else { >> > try { >> > this.blobCrypter = new BasicBlobCrypter(new >> File(key)); >> > } catch (IOException e) { >> > throw new SecurityException("Unable to load >>encryption >> key from file: " + key); >> > } >> > } >> > } else { >> > throw new SecurityException("Unable to find encryption >>key!"); >> > } >> > } >> > >> > @Override >> > @PreAuthorize("#personGadget.userId == principal.userId") >> > public SecurityToken getSecurityToken(PersonGadget personGadget) >> throws SecurityTokenException { >> > return this.getBlobCrypterSecurityToken(personGadget); >> > } >> > >> > @Override >> > @PreAuthorize("#personGadget.userId == principal.userId") >> > public String getEncryptedSecurityToken(PersonGadget personGadget) >> throws SecurityTokenException { >> > String encryptedToken = null; >> > >> > try { >> > BlobCrypterSecurityToken securityToken = >> this.getBlobCrypterSecurityToken(personGadget); >> > encryptedToken = this.encryptSecurityToken(securityToken); >> > } catch (Exception e) { >> > throw new SecurityTokenException("Error creating security >> token from person gadget", e); >> > } >> > >> > return encryptedToken; >> > } >> > >> > @Override >> > public SecurityToken decryptSecurityToken(String >> encryptedSecurityToken) throws SecurityTokenException { >> > SecurityToken securityToken; >> > >> > try { >> > log.debug("Decrypting security token: " + >> encryptedSecurityToken); >> > >> > //Remove the header container string and : >> > encryptedSecurityToken = >> encryptedSecurityToken.substring((CONTAINER + ":").length()); >> > >> > //Decrypt >> > securityToken = >> MitreBlobCrypterSecurityToken.decrypt(blobCrypter, CONTAINER, DOMAIN, >> encryptedSecurityToken); >> > } catch (Exception e) { >> > throw new SecurityTokenException("Error creating security >> token from encrypted string: " + encryptedSecurityToken, e); >> > } >> > >> > return securityToken; >> > } >> > >> > @Override >> > public String refreshEncryptedSecurityToken(String >> encryptedSecurityToken) throws SecurityTokenException { >> > try { >> > //Decrypt the current token >> > SecurityToken securityToken = >> this.decryptSecurityToken(encryptedSecurityToken); >> > >> > //Make sure the person is authorized to refresh this token >> > String userId = >> userService.getCurrentAuthenticatedUser().getUserId(); >> > if (!securityToken.getOwnerId().equalsIgnoreCase(userId)) { >> > throw new SecurityTokenException("Illegal attempt by >>user >> " + userId + " to refresh security token owned by " + >> securityToken.getOwnerId()); >> > } >> > >> > //Create a new PersonGadget instance from it so we can use >>it >> to generate a new encrypted token >> > PersonGadget personGadget = new >> PersonGadget(securityToken.getModuleId(), securityToken.getOwnerId()); >> > personGadget.setGadget(new Gadget(-1L, "", "", new >> URL(securityToken.getAppUrl()))); >> > >> > //Create and return the newly encrypted token >> > return getEncryptedSecurityToken(personGadget); >> > } catch (MalformedURLException e) { >> > throw new SecurityTokenException(e.getMessage(), e); >> > } >> > } >> > >> > private BlobCrypterSecurityToken >> getBlobCrypterSecurityToken(PersonGadget personGadget) throws >> SecurityTokenException { >> > Person user = userService.getCurrentAuthenticatedUser(); >> > >> > BlobCrypterSecurityToken securityToken = new >> BlobCrypterSecurityToken(blobCrypter, CONTAINER, DOMAIN); >> > >> securityToken.setAppUrl(personGadget.getGadget().getUrl().toString()); >> > securityToken.setModuleId(personGadget.getPersonGadgetId()); >> > securityToken.setOwnerId(personGadget.getUserId()); >> > securityToken.setViewerId(user.getUserId()); >> > securityToken.setTrustedJson(""); >> > >> > log.debug("Token created for personGadget " + >> personGadget.toString() + " and user " + user.toString()); >> > return securityToken; >> > } >> > >> > private String encryptSecurityToken(BlobCrypterSecurityToken >> securityToken) throws SecurityTokenException { >> > String encryptedToken = null; >> > >> > try { >> > encryptedToken = securityToken.encrypt(); >> > log.debug("Encrypted token created from security token: " + >> securityToken.toString() + " -- encrypted token is: " + encryptedToken); >> > } catch (Exception e) { >> > throw new SecurityTokenException("Error creating security >> token from person gadget", e); >> > } >> > >> > return encryptedToken; >> > } >> > } >> > >> > ---------- >> > >> > package org.apache.shindig.auth; >> > >> > import org.apache.shindig.common.crypto.BlobCrypter; >> > import org.apache.shindig.common.crypto.BlobCrypterException; >> > >> > /** >> > * This class is needed because the BlobCrypterSecurityToken.decrypt >> method is marked as protected and we >> > * need to be able to decrypt the security token in the container >> > */ >> > public class MitreBlobCrypterSecurityToken extends >> BlobCrypterSecurityToken { >> > public MitreBlobCrypterSecurityToken(BlobCrypter crypter, String >> container, String domain) { >> > super(crypter, container, domain); >> > } >> > >> > public static BlobCrypterSecurityToken decrypt(BlobCrypter crypter, >> String container, String domain, >> > String token) throws >> BlobCrypterException { >> > return BlobCrypterSecurityToken.decrypt(crypter, container, >> domain, token, null); >> > } >> > } >> > >> > ---------- >> > >> > I think some of the API's have changed on the Shindig side since this >> code was written (this was written for Shindig 2.0) but I think the >>concepts >> are still the same. >> > >> >> the >> >> gadget object like in [2]. In rave_opensocial.js we don't pass the >> gadget as >> >> an object to the container, just its url and render params. >> > >> > I think for now we could just add another securityToken property to >>the >> object we push onto the widgets array. I haven't yet looked into how we >> push this into the Shindig common container code however. Hopefully >>there's >> an easy way to do it... >> > >> >> >> >> Is there anyone who does know the right place where to handle the >> >> securityToken between the portal and container? >> >> >> >> >> >> [1] >> >> http://svn.apache.org/repos/asf/incubator/rave/donations/surfconext- >> >> portal/coin-portal/trunk/coin-portal- >> >> war/src/main/java/nl/surfnet/coin/portal/control/HomeController.java >> >> [2] >> >> http://svn.apache.org/repos/asf/incubator/rave/donations/surfconext- >> >> portal/coin-portal/trunk/coin-portal- >> >> war/src/main/webapp/js/coin/shindig/container.js >> >> >> >> Jasha Joachimsthal >> >> >> >> Europe - Amsterdam - Oosteinde 11, 1017 WT Amsterdam - +31(0)20 522 >>4466 >> >> US - Boston - 1 Broadway, Cambridge, MA 02142 - +1 877 414 4776 (toll >> free) >> >> >> >> www.onehippo.com >> >> >> Met vriendelijke groet, >> Okke Harsta >> >> Partner Zilverline >> Agile Management Consultant en Coach >> Lean Architect >> http://www.zilverline.com >> http://nl.linkedin.com/in/okkeharsta >> tel: +31 6 118 601 74 >> >> >>
