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


Reply via email to