Good news! The 3-legged oAuth works now on my machine but only with ugly
hacks to bypass a possible bug in Shindig [1]. So we'll first focus on other
parts of oAuth (like storing keys in the db, instead of reading it from a
json file in the war).

[1] http://markmail.org/thread/7r62z2ogkd4ad6pf

Jasha

On 28 June 2011 14:04, Franklin, Matthew B. <[email protected]> wrote:

> 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
> >>
> >>
> >>
>
>

Reply via email to