Register
Submit a solution
The challenge is finished.

Challenge Overview

Welcome to the simple web API server with OAuth2 authentication challenge ! 
This is the last challenge of our second stage ‘Develop Backend Services with Golang’

The Goal of this challenge is to add OAuth2 based authentication to the simple web API server we developed in the last challenge. We need you to develop a new API and update an existing API developed in the last challenge.

You will be implementing not all but a small part of OAuth2 authentication so you should be able to complete this challenge with what’s on this spec. if you would like to know the detail and concept of OAuth2, please check the website.

We would like all of you to use the webapi project as a starting point so all registrants could start under the same condition. The project is based on the winner's submission in the last challenge. You could download it in the forum. Please feel free to change/refactor it to meet the requirements of this challenge.

We will evaluate your submission based on the scorecard. We recommend you to check ‘Final Submission Guidelines’ section carefully. Please note that the scorecard and the guidelines are updated.

The person who gets the highest score wins. In case of a tie, the person to submit earlier wins.

If you have any questions, ask and get clarification in the forum.

P.S. - We plan to launch real world Golang challenges. Stay tuned !

API Call Flow

Let’s start with a simple sequence diagram to understand the overview.

Spec : oauth/access_token

This API is to issue an access token to a client. The client here means a program or service that uses the API server.

Endpoint

  • /api/2/domains/{domain name}/oauth/access_token
  • Use port 80. We would like to use other ports such as 8080 for testing
    • You could use the same port used for proxyauth endpoint
    • Https is required for OAuth2 but we don’t use it for this challenge

Request

Request Method

  • POST

Parameters

  • client_id
  • client_secret
  • grant_type (we just support “client_credentials” for this parameter)

ContentType

  • application/x-www-form-urlencoded

Sample

Request parameters.

  • domain name : topcoder.com
  • client_id : s6BhdRkqt3
  • client_secret : 7Fjfp0ZBr1KtDRbnfVdmIw
  • grant_type : client_credentials

Request to a server running on localhost with cURL

curl --data “client_id=s6BhdRkqt3&client_secret=7Fjfp0ZBr1KtDRbnfVdmIw&grant_type=client_credentials” http://localhost/api/2/domains/topcoder.com/oauth/access_token

Response

StatusCode

Use 200 to indicate that access token is obtained successfully. 400 is used to indicate invalid requests from clients. 404 is used when the domain name is not supported. 500 is used for system errors.

  • 200    Success
  • 400    Request Error
  • 404    No such domain
  • 500    Server error

Format

Response for status code 200. 

  • Only “bearer” is supported for “token_type”
  • “expires_in” is the lifetime in seconds of the access token. The default is 3600. If “expires_in” is 3600, the access token will expire in one hour from the time the response was generated. The lifetime should be configurable with command-line options.

{
    "access_token": "2YotnFZFEjr1zCsicMWpAA",
    "token_type": "bearer",
    "expires_in": 3600
}

Response for status code 400

{
    "error": "invalid_request"
}

“error” types

  • invalid_request
    • The request is missing a required parameter
  • invalid_client
    • Client authentication failed
  • unsupported_grant_type
    • The authorization grant type is not supported. We just support “client_credentials” this time.

No data should be returned for status code 404 and 500.

ContentType

In this challenge we follow the following rule. If the response has json body, the content type should be application/json, if not it should be text/plain. This rule is applied to proxyauth endpoint too.

  • application/json
    • For status code 200, 400
  • text/plain
    • For status code 404, 500

Authentication

Please use “domain.json” file in the webapi project. When you receive a request to topcoder.com domain with client_id and client_secret, you are supposed to find a record for the client_id under topcoder.com domain in domains.json, then compare the client_secret you get from the json file and the client_secret received. If they match, the authentication should succeed.

Access Token

If the authentication succeeds, you need to generate an access token for the client and save the token on the server.

Generate tokens

You could use ‘go-uuid’ package to generate access tokens.

Install

go get code.google.com/p/go-uuid

Sample code

import (
        "code.google.com/p/go-uuid/uuid"
        "encoding/base64"
)
u := uuid.New()
accessToken := base64.StdEncoding.EncodeToString([]byte(u))

Save tokens

You might have some options to choose to store access tokens on the server.

  • Save all access tokens for all the domains in one file.
  • Save all access tokens for one domain in one file.
  • Save one access token in one file.
  • Something else (do not use RDBMS for this challenge).

Note

  • No need to implement the “refresh-token” feature of OAuth2

Spec : proxyauth

We need to update “proxyauth” API to support OAuth2 authentication. We have 2 steps to update the endpoint for OAuth2 authentication.

1. Get access token from HTTP header
Get the access token sent via HTTP header. The following “Authorization” header should be sent.

Authorization: Bearer ZTExNDI1OTYtYjkwYS00ZjIyLTg2NjAtMzM3YjdkZmI3OTJj

“ZTExNDI1OTYtYjkwYS00ZjIyLTg2NjAtMzM3YjdkZmI3OTJj” is the access token.

2.  Validate access token
If you could find the same token on the server (that means the token has been issued), the authentication should succeed.

In case of the following validation errors, respond to the client with status code 400. 

  • Format of header is not valid / The access token is not sent
  • The access token is not valid
  • The access token is expired

Valdation errors response format
{
    "error": "error message"
}

error message should be human-readable and easy to detect the reason.

Content type should be application/json. (See ContentType section of oauth/access_token)

Sample Request

Request parameters.

  • domain name : topcoder.com
  • username : takumi
  • password : {SHA256}2QJwb00iyNaZbsEbjYHUTTLyvRwkJZTt8yrj4qHWBTU=
    • Original password is ‘ilovego’
  • access_token : ZTExNDI1OTYtYjkwYS00ZjIyLTg2NjAtMzM3YjdkZmI3OTJj

Request to a server running on localhost with cURL

  • curl --header “Authorization: Bearer ZTExNDI1OTYtYjkwYS00ZjIyLTg2NjAtMzM3YjdkZmI3OTJj” --data "username=takumi&password={SHA256}2QJwb00iyNaZbsEbjYHUTTLyvRwkJZTt8yrj4qHWBTU=" http://localhost/api/2/domains/topcoder.com/proxyauth

Data Store

We used “users.json” file in the last challenge. This time we use “domains.json” instead. “domains.json” has some data for OAuth2 authentication in addition to the data in users.json. You could find the file in the webapi project.

Test

Prepare your test script to cover the following cases.

Case 1 Success

  • domain : topcoder.com
  • Call oauth/access_token endpoint to obtain an access token 
    • Success with status code 200
  • Call proxyauth endpoint with the access token obtained
    • Success with status code 200

Case 2 Success

  • domain : appirio.com
  • Call oauth/access_token endpoint to obtain an access token 
    • Success with status code 200
  • Call proxyauth endpoint with the access token obtained
    • Success with status code 200

Case 3 Failure

  • domain : topcoder.com
  • Call oauth/access_token endpoint to obtain an access token
    • Failure with status code 400
    • error : invalid_request

Case 4 Failure

  • domain : appirio.com
  • Call oauth/access_token endpoint to obtain an access token
    • Failure with status code 400
    • error : invalid_client

Case 5 Failure

  • domain : appirio.com
  • Call oauth/access_token endpoint to obtain an access token
    • Failure with status code 400
    • error : unsupported_grant_type

Case 6 Failure

  • Call oauth/access_token endpoint to obtain an access token
    • Failure with status code 404

Case 7 Failure

  • Call proxyauth endpoint with no Authorization header.
    • Failure with status code 400

Case 8 Failure

  • Call proxyauth endpoint with an invalid access token
    • Failure with status code 400

Case 9 Failure

  • Call proxyauth endpoint with an expired access token
    • Failure with status code 400


Final Submission Guidelines

Code Guidelines

Follow practices mentioned in the articles below

  • http://golang.org/doc/effective_go.html
  • https://code.google.com/p/go-wiki/wiki/CodeReviewComments#Go_Code_Review_Comments

We have one note specific to this challenge.

  • Use  ‘lower_case_with_underscore’ name for package, file or directory. However, try to avoid underscores and prefer short names

Submission Deliverables

  • Source code
  • Format your code with ‘gofmt’ command.
  • Test script such as a shell script that covers the test cases
    • It is not mandatory to verify results in the script (it is better to verify though).
  • Updated test code (*_test.go files).
  • Your README to explain your deliverables

External Libraries

  • Please do not use external libraries except go-uuid package.

ELIGIBLE EVENTS:

2015 topcoder Open

Review style

Final Review

Community Review Board

Approval

User Sign-Off

ID: 30046224