Supertext Home
Chief of the System Blog

Archive for the 'API' Category


How to get a Supertext API Token

Thursday, September 13th, 2012

image

First, login to your Supertext AG account with your normal username and password:

https://www.supertext.ch/de/login

Then click on Einstellungen below Konto.

Scroll all the way down until you see the part “API Authentifizierungs Token”

 

 

image

Here you see your token. Use this and your username in the system that sends requests to the Supertext API.

 

If you don’t have an account yet, you can create one here:

https://www.supertext.ch/de/signup


Social Media Kit Integration FAQ

Monday, August 20th, 2012

Users of the Social Media Kit  can now directly order translations from Supertext as mentioned in our main Blogpost. This post is a little manual and FAQ.

 

Setup

setup

First, make sure that the language settings are setup according to your needs. We often offer the choice between different types of the same language. E.g. with English, you can choose between UK, US and Ireland.

2012-07-18_08h26_29

For this you have to go to “Einstellungen SMK” –> Dienste –> Supertext Übersetzungen –> Bearbeiten” and then to “Sprachen Einstellungen”.

 

Usage

You can send an an article into translation either directly from the article overview, by pressing on the plus sign.

2012-07-18_08h31_56

Or inside an article with the “Sprache” menu bar on the right.

2012-07-18_08h27_12

Afterwards you can choose which parts of your post you want to have translated and decide when you need it back. Adding a comment always helps us with the translation.

2012-07-18_08h27_34

 

Once the post is translated, it will automatically appear in the Social Media Kit and you will get an email from us. In the meantime the post looks like this:

2012-07-18_08h31_21

 

Technical functionality

image

The Blogwerk Plugin uses our REST API. My blog post should answer most of your questions.

 

Blogwerk FAQ

I had to change the original article, but already sent it into translation. Can I resend it?

Call us as quickly as possible +41 43 500 33 80 and we can stop and delete or order on our side. On your side you need to completely remove the still empty post (you also need to remove it from the recycle bin (Papierkorb). Then you should see the option to order it again.

 

I ordered an English translation and Supertext delivered it in UK English. But I need US English.

We do both UK and US English. You just have to adjust your settings as mentioned at the beginning of this post. If such an order is still in translation with us, just call us, we can change it.

 

 

Supertext FAQ

Can I group different articles into one Trados Project?

Yes.

 

Can I use one source file for multiple languages in a Trados Project?

Yes

 

Can I resend a finished translation again?

Yes, as long as the post has not been published yet.

 

How can I see if the post has been published?

Log into the local.ch blog and go to the article. On the upper right corner is a menu called “Veröffentlichen”. There you see the status.

image


Basic Http Authorization for Web API in MVC 4 Beta

Thursday, April 19th, 2012

A little while ago I posted a solution to do Basic Http Authorization with the Web API Preview 6. Web API got then merged into the next ASP.NET MVC 4 Beta Release and in the process has changed a lot.

Since my old approach did not work anymore, I had to create something new.

 

Usage:

public class OrderController : ApiController
{
    // GET /api/orders/5
    [BasicHttpAuthorizeAttribute(RequireAuthentication = true)]
    public string Get(int id, string communicationLang)
    {
        //do your API Stuff
    }
}

And the Authentication class itself:

public class BasicHttpAuthorizeAttribute : System.Web.Http.AuthorizeAttribute  
{
    bool requireSsl = Convert.ToBoolean(ConfigurationManager.AppSettings["RequireSsl"]);
 
    public bool RequireSsl
    {
        get { return requireSsl; }
        set { requireSsl = value; }
    }
 
 
    bool requireAuthentication = true;
 
    public bool RequireAuthentication
    {
        get { return requireAuthentication; }
        set { requireAuthentication = value; }
    }
 
 
    /// <summary>
    /// For logging with Log4net.
    /// </summary>
    private static readonly ILog log = LogManager.GetLogger(typeof(BasicHttpAuthorizeAttribute));
 
 
    public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)        
    {
        //actionContext.Request
 
        if (Authenticate(actionContext) || !RequireAuthentication)
        {
            return;
        }
        else
        {
            HandleUnauthorizedRequest(actionContext);
        }
    }
 
    protected override void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actionContext)
    {
        var challengeMessage = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
        challengeMessage.Headers.Add("WWW-Authenticate", "Basic");
        throw new HttpResponseException(challengeMessage);
        //throw new HttpResponseException();
    }
 
 
    private bool Authenticate(System.Web.Http.Controllers.HttpActionContext actionContext) //HttpRequestMessage input)
    {
        if (RequireSsl && !HttpContext.Current.Request.IsSecureConnection && !HttpContext.Current.Request.IsLocal)
        {
            log.Error("Failed to login: SSL:" + HttpContext.Current.Request.IsSecureConnection);
            return false;
        }
 
        if (!HttpContext.Current.Request.Headers.AllKeys.Contains("Authorization")) return false;
 
        string authHeader = HttpContext.Current.Request.Headers["Authorization"];
 
        IPrincipal principal;
        if (TryGetPrincipal(authHeader, out principal))
        {
            HttpContext.Current.User = principal;
            return true;
        }
        return false;
    }
 
 
    private bool TryGetPrincipal(string authHeader, out IPrincipal principal)
    {
        var creds = ParseAuthHeader(authHeader);
        if (creds != null)
        {
            if (TryGetPrincipal(creds[0], creds[1], out principal)) return true;
        }
 
        principal = null;
        return false;
    }
 
 
    private string[] ParseAuthHeader(string authHeader)
    {
        // Check this is a Basic Auth header 
        if (authHeader == null || authHeader.Length == 0 || !authHeader.StartsWith("Basic")) return null;
 
        // Pull out the Credentials with are seperated by ':' and Base64 encoded 
        string base64Credentials = authHeader.Substring(6);
        string[] credentials = Encoding.ASCII.GetString(Convert.FromBase64String(base64Credentials)).Split(new char[] { ':' });
 
        if (credentials.Length != 2 || string.IsNullOrEmpty(credentials[0]) || string.IsNullOrEmpty(credentials[0])) return null;
 
        // Okay this is the credentials 
        return credentials;
    }
 
 
    private bool TryGetPrincipal(string username, string password, out IPrincipal principal)
    {
        // this is the method that does the authentication 
 
        //users often add a copy/paste space at the end of the username
        username = username.Trim();
        password = password.Trim();
 
        //TODO
        //Replace this with your own Authentication Code
        Person person = AccountManagement.ApiLogin(username, password);
 
        if (person != null)
        {
            // once the user is verified, assign it to an IPrincipal with the identity name and applicable roles
            principal = new GenericPrincipal(new GenericIdentity(username), System.Web.Security.Roles.GetRolesForUser(username));
            return true;
        }
        else
        {
            if (!String.IsNullOrWhiteSpace(username))
            {
                log.Error("Failed to login: username=" + username + "; password=" + password);
            }
            principal = null;
            return false;
        }
    }
}

You will have to adjust the TryGetPrincipal() method to include our own Authorization code that works with your system. Or maybe plain normal ASP.NET Provider Authentication is enough in your case.

You can download the code from github:
https://github.com/rblaettler/BasicHttpAuthorization


Install the Translation Management Tool for Drupal

Wednesday, February 29th, 2012

First, you have to download the following modules:

 

Translation Management Tool (the most important part)

http://drupal.org/project/tmgmt

 

TMGMT Translator Supertext

http://drupal.org/project/tmgmt_supertext

This is the connector between the Translation Management Tool (tmgmt) and the Translation Agency Supertext.

TMGMT Translator Microsoft

http://drupal.org/project/tmgmt_microsoft 

Not necessary, but a good way to test your installation.

 

Entity API (the latest dev version)

http://drupal.org/project/entity

 

Chaos tool suite (ctools)

http://drupal.org/project/ctools

 

Views (3.3 or newer)

http://drupal.org/project/views

 

Views Bulk Operations (VBO)

http://drupal.org/project/views_bulk_operations

 

Internationalization

http://drupal.org/project/i18n

Which turn needs the Variable module:

http://drupal.org/project/variable

 

Rules

http://drupal.org/project/rules

 

You can either install these modules via Administration –> Modules or by copying them into /yourwebsite/sites/all/modules.

 

Now, go to back to Administration –> Modules.

Here you need to enable the following parts:

 

Core

  • Content translation
  • Locale

 

Chaos tool suite

  • Chaos tools

 

Multilingual – Internationalization

  • Internationalization
  • Multilingual content
  • String translation
  • Variable translation

 

Other

  • Entity API
  • Entity tokens
  • Variable

 

Rules

  • Rules
  • Rules UI

 

Translation Management

  • Content Source
  • Content Source User Interface
  • Entity Source
  • Microsoft Translator
  • Translation Management Core
  • Translation Management Field
  • Translation Management UI
  • Supertext Translator

Views

  • Views
  • Views Bulk Operations
  • Views UI

 

A good starting tutorial to configure Drupal for Multilingual Content is here: http://drupal.org/node/1268692

 

But there are mainly two core things to configure

First: You have to add multiple languages.

Go to:

Home » Administration » Configuration » Regional and language
And add one or two languages.

 

Second: Configure your content to be translatable.

Go to:

Home » Administration » Structure » Content types
Click on either “Article” or “Basic page” » edit and then choose “Publishing options”.

image

Select “Enabled, with translation” and then switch to “Multilingual settings” and configure accordingly.

 

Now you should be ready!

Add some content and you should see the “Translate” tab besides View and Edit.

image

 

But first you have to configure a translator.

For testing the Microsoft translator is perfect. Go to:

Home » Administration » Configuration » Regional and language » Translation Management.

Click on the “Transators” tab and then on edit by the Microsoft translator. Add your API key (you can get it from the link below the textbox).

Let’s go back to your content page and click in Translate.

image

We need a German translation and then click on “Request translation” (add translation is if you want to translate it manually).

image

In the Translator dropdown we choose Microsoft translator. And then click on “Submit to translator”.

Since this is a machine translation, we get the result immediately.

image

Click on “Needs review” under “Pending Translations” and just accept the translation.

Done!


The Supertext REST API

Wednesday, February 22nd, 2012

What?

In the early days, the internet solely consisted of simple static webpages. With the Dot-Com boom (or bubble), the webpages changed into complex and dynamic applications. But they were all like islands. There was no connection whatsoever between them. But now, companies like Facebook and Twitter loosened up and enable other people to build applications, allowing to connect to them and add their own functionality.

Until now, Supertext has – besides some specific integrations – been an island too. But that’s over now. We have an open and public API allowing other applications to directly integrate with Supertext.

 

Why?

Who wants to integrate with Supertext you wonder? And why? Actually, lots of people. It started 4 years ago with Akero, a now defunct CMS system. Akero users were able to directly order translations and text editing from inside their CMS and got the final text delivered back into it. Clearly, this wasn’t enough to make Akero a success, but the need to order directly from 3rd party systems remained evident until today.

 

How?

Now it gets a bit technical. The Supertext API is built as a REST API that understands JSON or XML. Almost all modern open APIs are built alike. This means that they’re accessible with normal HTTP calls and they enable you to send and receive human readable JSON or XML messages.

 

Who?

Authentication is done via normal Basic HTTP Authentication. Most other APIs just use a token or your username and password. We decided to work with a username in combination with a custom token. You can get this token from your Supertext Account Settings page. If you don’t have a Supertext account, you can establish one on the sign up page.

 

Where?

Please send an e-mail to remyATsupertextDOTch and I will send you the URL for the sandbox and the live system.

 

Which?

Currently we support the functionality listed below, basically you can get quotes and make orders.

You can add the below parameter to most calls, to specify in what language you get the results back:

communicationlang={communicationlang}

Adding it is optional. Otherwise the response defaults to English.  We currently support CHF and EUR. For a quote you can choose between the two currencies, but if you create an order, whatever is set in your account will be used.

 

For the configuration

/translation/languagemapping/{language}

GET – No authentication necessary.

Helps you map a language in your system to one we support.

E.g. your CMS is setup for ‘de’ (German). We don’t actually translate into German, we translate into German for Switzerland, for Germany or for Austria. So this method returns you a list of possible matches. For {language} = ‘de’ the result would look like this:

{
  "Languages":
  [
    {
      "Iso":"de-CH",
      "Name":"German (CH)"
    },
    {
      "Iso":"de-DE",
      "Name":"German (DE)"
    },
    {
      "Iso":"de-AT",
      "Name":"German (AT)"
    }
  ],
  "Supported":false
}

 

To get a quote

/translation/quote

POST – authentication optional

Getting a quote over the API works in pretty much the same way as if you were using our normal website to order a translation (give it a try). Send us the text and in return, you see a list of possible delivery deadlines and prices per service levels  (translation/adaptation). You can send us the content structured in groups and items, which is helpful if you have CMS that is built that way. E.g. a group could be a page, items are title, content, metatags and so on. The following is a possible JSON for this call:

{
  "ContentType":"text\/html",
  "Currency":"chf",
  "Groups":
  [
    {
      "GroupId":"Group1",
      "Items":
      [
        {
          "Content":"This is the content of group 1",
          "Id":"1"
        },
        {
          "Content":"This is more content  of group 1",
          "Id":"2"
        }
      ]
    },
    {
      "GroupId":"Group2",
      "Items":
      [
        {
          "Content":"This is the content  of group 2",
          "Id":"1"
        },
        {
          "Content":"This is more content  of group 2",
          "Id":"2"
        }
      ]
    }
  ],
  "SourceLang":"de-CH",
  "TargetLang":"en-US",
}

 

And this could be what you get in return:

 

{
  "Currency":"CHF",
  "WordCount": 123,
  "Options":
  [
    {
      "DeliveryOptions":
      [
        {
          "DeliveryDate":"2012-02-22T09:25:46.0000000Z",
          "DeliveryId":1,
          "Name":"6h",
          "Price":124
        },
        {
          "DeliveryDate":"2012-02-22T15:25:46.0000000Z0",
          "DeliveryId":2,
          "Name":"24h",
          "Price":110
        },
        {
          "DeliveryDate":"2012-02-23T15:25:46.0000000Z",
          "DeliveryId":3,
          "Name":"48h",
          "Price":96
        },
        {
          "DeliveryDate":"2012-02-24T15:25:46.0000000Z",
          "DeliveryId":4,
          "Name":"3 Days",
          "Price":82
        },
        {
          "DeliveryDate":"2012-02-28T15:25:46.0000000Z",
          "DeliveryId":5,
          "Name":"1 Week",
          "Price":69
        }
      ],
      "Description":"For an exact reproduction of the original text, the translated text is checked by a proofreader.",
      "Name":"Translation",
      "OrderTypeId":6,
      "ShortDescription":"4-eye principle."
    },
    {
      "DeliveryOptions":
      [
        {
          "DeliveryDate":"2012-02-22T15:25:46.0000000Z",
          "DeliveryId":2,
          "Name":"24h",
          "Price":254
        },
        {
          "DeliveryDate":"2012-02-23T15:25:46.0000000Z",
          "DeliveryId":3,
          "Name":"48h",
          "Price":222
        },
        {
          "DeliveryDate":"2012-02-24T15:25:46.0000000Z",
          "DeliveryId":4,
          "Name":"3 Days",
          "Price":190
        },
        {
          "DeliveryDate":"2012-02-28T15:25:46.0000000Z",
          "DeliveryId":5,
          "Name":"1 Week",
          "Price":159
        }
      ],
      "Description":"For the translation to sound as good as the original, the translated text is stylistically post-edited.",
      "Name":"Adaptation",
      "OrderTypeId":7,
      "ShortDescription":"6-eye principle."
    }
  ]
}

 

And to make an order

/translation/order

POST – authentication necessary

When making an order, we need more details than we need for a quote. At first, we need to know which quote you’ve chosen. So you have to add the OrderTypeId and the DeliveryId.

Very important is the CallbackUrl. After the translation job is finished, we will call this URL with a similar JSON order object and write back the translated content into your system.

We strongly advise you to use the following fields:

OrderName: Some short description about this order. E.g. “Spring sale”

ReferenceData: Anything you need to identify this order later (besides the GroupId and the Id for the Content field). And optionally, you could add some security token so that not everybody with access to the callback URL can update your system.

Referrer: Name of your website or system. E.g. Supertext US Website

 

{
  "CallbackUrl":"http://localhost:65346/API/ApiCallbackExample.aspx",
  "ContentType":"text\/html",
  "Currency":"chf",
  "DeliveryId":1,
  "OrderName":"Some title",  
  "AdditionalInformation":"Please make sure you always translate this like that.",
  "OrderTypeId":6,
  "ReferenceData":"NodeId:4ee69461-1c8d-4fbe-9d77-7d05e46bc4a8",
  "Referrer":"Supertext Magazin",
  "SourceLang":"de-CH",
  "TargetLang":"en-US",
  "WordCount":0,
  "Groups":
  [
    {
      "Context":"Some Node",
      "GroupId":"Group1",
      "Items":
      [
        {
          "Comment":null,
          "Content":"This is the content of group 1",
          "Context":null,
          "Id":"1"
        },
        {
          "Comment":null,
          "Content":"This is new content of group 1",
          "Context":null,
          "Id":"2"
        }
      ]
    },
    {
      "Context":"Some other Node",
      "GroupId":"Group2",
      "Items":
      [
        {
          "Comment":null,
          "Content":"This is the content of group 2",
          "Context":null,
          "Id":"1"
        },
        {
          "Comment":null,
          "Content":"This is new content of group 2",
          "Context":null,
          "Id":"2"
        }
      ]
    }
  ]
}

 

You get back another order object with the order Id, a price, order and delivery date. All the other data is just for your reference.

 

{
  "Id":12023,
  "OrderDate":"2012-02-09T13:43:46.0000000Z",
  "Deadline":"2012-02-09T13:43:46.0000000Z",
  "Price":199,
  "Currency":"chf",
  "DeliveryId":1,
  "OrderTitle":"Some title",
  "OrderTypeId":6,
  "ReferenceData":"NodeId:4ee69461-1c8d-4fbe-9d77-7d05e46bc4a8",
  "SourceLang":"de-CH",
  "TargetLang":"en-US",
  "Status":"New"
}

 

Status

/translation/order/{id}

GET – authentication necessary

In order to track the status of an order, just use the Id you got after you submitted the order. You will get an Order object like the one above as a return.

 

Callback
In order to get your translation back from us, you need to provide us with a callback URL.
When you create the order, you have to use the field CallbackUrl. This URL needs to accept a JSON of the type Order (as shown above). That Order object will contain your translation in the Groups and Item fields with the same Id’s, so you can map them back to your own datastructure.
We recommend that you use the ReferenceData field as some type of authentication. For example you could use a combination of an internal Id and the MD5 hash of this Id. We will return the ReferenceData field in our callback. You can then just check if the Hash matches to make sure nobody else is writing into your system.

 

Dates

All dates are in UTC and in the ISO_8601 format:

2012-05-03T12:09:46.0000000Z

If you are using .NET just use Convert.ToDateTime(), this will automatically convert from the UTC time to your local time.


Basic Authentication with WCF Web API Preview 6

Friday, February 3rd, 2012

UDATE:
I’ve built a new version for the MVC Web API:
http://remy.supertext.ch/2012/04/basic-http-authorization-for-web-api-in-mvc-4-beta/

One should not believe it, but it seems that there is no official way to use your own version of Basic HTTP Authentication with the WCF Web API in an MVC Web Application yet. So, now that I’ve used all possible necessary keywords we can dive right in.

 

We are using a custom ASP.NET Membership provider and the REST API should work with a token over Basic HTTP Authentication (like Basecamp). So, the built in Windows Basic Authentication is not an option.

After scanning dozens of posts on Stackoverflow and other resources I realized that either I have to go with the WCF REST Contrib library or with Open Rasta. But since I already started with the WCF Web API Preview 6 to build my REST API (and it worked fine so far) I didn’t want to switch now.

The most promising solution I found was from jslaybaugh. He is basically using a custom version of the [Authorize] attribute from the normal MVC framework and somehow integrated it all with Ninject. For some reason I didn’t got it working. In general AuthorizeAttribute and action filters are MVC specific. For WCF we have the HttpOperationHandler or the DelegatingHandler. They have their specific uses and advantages. Some info about his from Glenn Block.

I’ve decided to go with the HttpOperationHandler and found a good example from Phil Haack where he implements a Role authorization Module, that also works with Attributes, so we can implement something very similar to the AuthorizeAttribute of MVC. There are simpler solutions, e.g. you could just check this inside your Controller, but Craig Stuntz has some good points about why this is a bad idea.

So, let’s look at the code (which is a potpourri of all the above examples):

[AttributeUsage(AttributeTargets.Method)]
public class BasicHttpAuthorizationAttribute : Attribute
{
    bool requireSsl = true;
 
    public bool RequireSsl
    {
        get { return requireSsl; }
        set { requireSsl = value; }
    }
}

With this simple BasicHttpAuthorizationAttribute class we can achieve the the attribute functionality. So we can use it like this:

[BasicHttpAuthorization(RequireSsl = true)]
[WebGet(UriTemplate = "")]
public IEnumerable Get()
{

 

The RequireSsl is just an example property, you could also do a role membership check in the same way.

But the most important part is the implementation of the HttpOperationHandler. We pass the BasicHttpAuthorizationAttribute as an argument.

There are three main points that deserve attention here:

  1. If the user is not authenticated yet or provides the wrong credentials we return a HttpResponseException in the OnHandle method. We set the status code to 401 and add the WWW-Authenticate = Basic header. This creates the functionality, where the browser asks for a username/password and then automatically resends the request.
  2. In ParseAuthHeader we get the username and password out of the request. You can  then use this info with in your own way. For example with your own custom membership provider.
  3. If the user can access this method, we create a GenericPrincipal and assign it to HttpContext.Current.User. Afterwards you can then just use your normal MemberShip and RoleProvider like in every normal ASP.NET application.

Other than that, there is not much magic in here.

public class BasicHttpAuthorizationOperationHandler : HttpOperationHandler
{
 
    BasicHttpAuthorizationAttribute basicHttpAuthorizationAttribute;
 
    public BasicHttpAuthorizationOperationHandler(BasicHttpAuthorizationAttribute authorizeAttribute)
        : base("response")
    {
        basicHttpAuthorizationAttribute = authorizeAttribute;
    }
 
    protected override HttpRequestMessage OnHandle(HttpRequestMessage input)
    {
        if (Authenticate(input))
        {
            return input;
        }
        else
        {
            var challengeMessage = new HttpResponseMessage(HttpStatusCode.Unauthorized);
            challengeMessage.Headers.Add("WWW-Authenticate", "Basic");
            throw new HttpResponseException(challengeMessage);
        }
    }
 
    private bool Authenticate(HttpRequestMessage input)
    {
        if (basicHttpAuthorizationAttribute.RequireSsl &amp;&amp; !HttpContext.Current.Request.IsSecureConnection &amp;&amp; !HttpContext.Current.Request.IsLocal) return false;
 
        if (!HttpContext.Current.Request.Headers.AllKeys.Contains("Authorization")) return false;
 
        string authHeader =  HttpContext.Current.Request.Headers["Authorization"];
 
        IPrincipal principal;
        if (TryGetPrincipal(authHeader, out principal))
        {
            HttpContext.Current.User = principal;
            return true;
        }
        return false;
    }
 
    private bool TryGetPrincipal(string authHeader, out IPrincipal principal)
    {
        var creds = ParseAuthHeader(authHeader);
        if (creds != null)
        {
            if (TryGetPrincipal(creds[0], creds[1], out principal)) return true;
        }
 
        principal = null;
        return false;
    }
 
    private string[] ParseAuthHeader(string authHeader)
    {
        // Check this is a Basic Auth header
        if (authHeader == null || authHeader.Length == 0 || !authHeader.StartsWith("Basic")) return null;
 
        // Pull out the Credentials with are seperated by ':' and Base64 encoded
        string base64Credentials = authHeader.Substring(6);
        string[] credentials = Encoding.ASCII.GetString(Convert.FromBase64String(base64Credentials)).Split(new char[] { ':' });
 
        if (credentials.Length != 2 || string.IsNullOrEmpty(credentials[0]) || string.IsNullOrEmpty(credentials[0])) return null;
 
        // Okay this is the credentials
        return credentials;
    }
 
    private bool TryGetPrincipal(string userName, string password, out IPrincipal principal)
    {
        // this is the method that does the authentication
        // you can replace this with whatever logic you'd use, but proper separation would put the
 
        if (userName.Equals("remy@test.ch") &amp;&amp; password.Equals("test"))
        {
            // once the user is verified, assign it to an IPrincipal with the identity name and applicable roles
            // Example:
            //principal = new GenericPrincipal(new GenericIdentity(userName), System.Web.Security.Roles.GetRolesForUser(userName));
 
            principal = new GenericPrincipal(new GenericIdentity(userName), new string[] {"Admin", "User"});
 
            return true;
        }
        else
        {
            principal = null;
            return false;
        }
    }
}

Last but not least, we need to hook up our HttpOperationHandler with the BasicHttpAuthorizationAttribute object and route. For this we create a custom WebApiConfiguration and use a class extension to do the wiring. Honestly, I’m not really sure what is going on here, but it works :-)

public class ApiConfiguration : WebApiConfiguration
{
    public ApiConfiguration()
    {
        EnableTestClient = true;
 
        RequestHandlers = (c, e, od) =&gt;
        {
            // TODO: Configure request operation handlers
        };
 
        this.AppendAuthorizationRequestHandlers();
    }
}
 
public static class ConfigExtensions
{
    public static void AppendAuthorizationRequestHandlers(this WebApiConfiguration config)
    {
        var requestHandlers = config.RequestHandlers;
        config.RequestHandlers = (c, e, od) =&gt;
        {
            if (requestHandlers != null)
            {
                requestHandlers(c, e, od); // Original request handler
            }
            var authorizeAttribute = od.Attributes.OfType()
              .FirstOrDefault();
            if (authorizeAttribute != null)
            {
                c.Add(new BasicHttpAuthorizationOperationHandler(authorizeAttribute));
            }
        };
    }
}

And we pass this configuration in the global.asax.cs to the route handler:

public static void RegisterRoutes(RouteCollection routes)
{
    var config = new ApiConfiguration();
 
    routes.Add(new ServiceRoute("example", new HttpServiceHostFactory() { Configuration = config }, typeof(ExampleAPI)));
 
}

That is it. You can download the example project here: BasicAuthenticationWithWcfWebAPI.zip

 

Please let me know if this works for you and specially, if you find ways to improve it.

  • Topics
  • Archive
  • Subscribe
  • Facebook
  • Twitter