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