Code Monkey home page Code Monkey logo

appauth-android's Introduction

AppAuth for Android

Download Javadocs Build Status codecov.io

AppAuth for Android is a client SDK for communicating with OAuth 2.0 and OpenID Connect providers. It strives to directly map the requests and responses of those specifications, while following the idiomatic style of the implementation language. In addition to mapping the raw protocol flows, convenience methods are available to assist with common tasks like performing an action with fresh tokens.

The library follows the best practices set out in RFC 8252 - OAuth 2.0 for Native Apps, including using Custom Tabs for authorization requests. For this reason, WebView is explicitly not supported due to usability and security reasons.

The library also supports the PKCE extension to OAuth which was created to secure authorization codes in public clients when custom URI scheme redirects are used. The library is friendly to other extensions (standard or otherwise) with the ability to handle additional parameters in all protocol requests and responses.

A talk providing an overview of using the library for enterprise single sign-on (produced by Google) can be found here: Enterprise SSO with Chrome Custom Tabs.

Download

AppAuth for Android is available on MavenCentral

implementation 'net.openid:appauth:<version>'

Requirements

AppAuth supports Android API 16 (Jellybean) and above. Browsers which provide a custom tabs implementation are preferred by the library, but not required. Both Custom URI Schemes (all supported versions of Android) and App Links (Android M / API 23+) can be used with the library.

In general, AppAuth can work with any Authorization Server (AS) that supports native apps as documented in RFC 8252, either through custom URI scheme redirects, or App Links. AS's that assume all clients are web-based or require clients to maintain confidentiality of the client secrets may not work well.

Demo app

A demo app is contained within this repository. For instructions on how to build and configure this app, see the demo app readme.

Conceptual overview

AppAuth encapsulates the authorization state of the user in the net.openid.appauth.AuthState class, and communicates with an authorization server through the use of the net.openid.appauth.AuthorizationService class. AuthState is designed to be easily persistable as a JSON string, using the storage mechanism of your choice (e.g. SharedPreferences, sqlite, or even just in a file).

AppAuth provides data classes which are intended to model the OAuth2 specification as closely as possible; this provides the greatest flexibility in interacting with a wide variety of OAuth2 and OpenID Connect implementations.

Authorizing the user occurs via the user's web browser, and the request is described using instances of AuthorizationRequest. The request is dispatched using performAuthorizationRequest() on an AuthorizationService instance, and the response (an AuthorizationResponse instance) will be dispatched to the activity of your choice, expressed via an Intent.

Token requests, such as obtaining a new access token using a refresh token, follow a similar pattern: TokenRequest instances are dispatched using performTokenRequest() on an AuthorizationService instance, and a TokenResponse instance is returned via a callback.

Responses can be provided to the update() methods on AuthState in order to track and persist changes to the authorization state. Once in an authorized state, the performActionWithFreshTokens() method on AuthState can be used to automatically refresh access tokens as necessary before performing actions that require valid tokens.

Implementing the authorization code flow

It is recommended that native apps use the authorization code flow with a public client to gain authorization to access user data. This has the primary advantage for native clients that the authorization flow, which must occur in a browser, only needs to be performed once.

This flow is effectively composed of four stages:

  1. Discovering or specifying the endpoints to interact with the provider.
  2. Authorizing the user, via a browser, in order to obtain an authorization code.
  3. Exchanging the authorization code with the authorization server, to obtain a refresh token and/or ID token.
  4. Using access tokens derived from the refresh token to interact with a resource server for further access to user data.

At each step of the process, an AuthState instance can (optionally) be updated with the result to help with tracking the state of the flow.

Authorization service configuration

First, AppAuth must be instructed how to interact with the authorization service. This can be done either by directly creating an AuthorizationServiceConfiguration instance, or by retrieving an OpenID Connect discovery document.

Directly specifying an AuthorizationServiceConfiguration involves providing the URIs of the authorization endpoint and token endpoint, and optionally a dynamic client registration endpoint (see "Dynamic client registration" for more info):

AuthorizationServiceConfiguration serviceConfig =
    new AuthorizationServiceConfiguration(
        Uri.parse("https://idp.example.com/auth"), // authorization endpoint
        Uri.parse("https://idp.example.com/token")); // token endpoint

Where available, using an OpenID Connect discovery document is preferable:

AuthorizationServiceConfiguration.fetchFromIssuer(
    Uri.parse("https://idp.example.com"),
    new AuthorizationServiceConfiguration.RetrieveConfigurationCallback() {
      public void onFetchConfigurationCompleted(
          @Nullable AuthorizationServiceConfiguration serviceConfiguration,
          @Nullable AuthorizationException ex) {
        if (ex != null) {
          Log.e(TAG, "failed to fetch configuration");
          return;
        }

        // use serviceConfiguration as needed
    }
});

This will attempt to download a discovery document from the standard location under this base URI, https://idp.example.com/.well-known/openid-configuration. If the discovery document for your IDP is in some other non-standard location, you can instead provide the full URI as follows:

AuthorizationServiceConfiguration.fetchFromUrl(
    Uri.parse("https://idp.example.com/exampletenant/openid-config"),
    new AuthorizationServiceConfiguration.RetrieveConfigurationCallback() {
        ...
    }
});

If desired, this configuration can be used to seed an AuthState instance, to persist the configuration easily:

AuthState authState = new AuthState(serviceConfig);

Obtaining an authorization code

An authorization code can now be acquired by constructing an AuthorizationRequest, using its Builder. In AppAuth, the builders for each data class accept the mandatory parameters via the builder constructor:

AuthorizationRequest.Builder authRequestBuilder =
    new AuthorizationRequest.Builder(
        serviceConfig, // the authorization service configuration
        MY_CLIENT_ID, // the client ID, typically pre-registered and static
        ResponseTypeValues.CODE, // the response_type value: we want a code
        MY_REDIRECT_URI); // the redirect URI to which the auth response is sent

Other optional parameters, such as the OAuth2 scope string or OpenID Connect login hint are specified through set methods on the builder:

AuthorizationRequest authRequest = authRequestBuilder
    .setScope("openid email profile https://idp.example.com/custom-scope")
    .setLoginHint("[email protected]")
    .build();

This request can then be dispatched using one of two approaches.

a startActivityForResult call using an Intent returned from the AuthorizationService, or by calling performAuthorizationRequest and providing pending intent for completion and cancelation handling activities.

The startActivityForResult approach is simpler to use but may require more processing of the result:

private void doAuthorization() {
  AuthorizationService authService = new AuthorizationService(this);
  Intent authIntent = authService.getAuthorizationRequestIntent(authRequest);
  startActivityForResult(authIntent, RC_AUTH);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  if (requestCode == RC_AUTH) {
    AuthorizationResponse resp = AuthorizationResponse.fromIntent(data);
    AuthorizationException ex = AuthorizationException.fromIntent(data);
    // ... process the response or exception ...
  } else {
    // ...
  }
}

If instead you wish to directly transition to another activity on completion or cancelation, you can use performAuthorizationRequest:

AuthorizationService authService = new AuthorizationService(this);

authService.performAuthorizationRequest(
    authRequest,
    PendingIntent.getActivity(this, 0, new Intent(this, MyAuthCompleteActivity.class), 0),
    PendingIntent.getActivity(this, 0, new Intent(this, MyAuthCanceledActivity.class), 0));

The intents may be customized to carry any additional data or flags required for the correct handling of the authorization response.

Capturing the authorization redirect

Once the authorization flow is completed in the browser, the authorization service will redirect to a URI specified as part of the authorization request, providing the response via query parameters. In order for your app to capture this response, it must register with the Android OS as a handler for this redirect URI.

We recommend using a custom scheme based redirect URI (i.e. those of form my.scheme:/path), as this is the most widely supported across all versions of Android. To avoid conflicts with other apps, it is recommended to configure a distinct scheme using "reverse domain name notation". This can either match your service web domain (in reverse) e.g. com.example.service or your package name com.example.app or be something completely new as long as it's distinct enough. Using the package name of your app is quite common but it's not always possible if it contains illegal characters for URI schemes (like underscores) or if you already have another handler for that scheme - so just use something else.

When a custom scheme is used, AppAuth can be easily configured to capture all redirects using this custom scheme through a manifest placeholder:

android.defaultConfig.manifestPlaceholders = [
  'appAuthRedirectScheme': 'com.example.app'
]

Alternatively, the redirect URI can be directly configured by adding an intent-filter for AppAuth's RedirectUriReceiverActivity to your AndroidManifest.xml:

<activity
        android:name="net.openid.appauth.RedirectUriReceiverActivity"
        tools:node="replace">
    <intent-filter>
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE"/>
        <data android:scheme="com.example.app"/>
    </intent-filter>
</activity>

If an HTTPS redirect URI is required instead of a custom scheme, the same approach (modifying your AndroidManifest.xml) is used:

<activity
        android:name="net.openid.appauth.RedirectUriReceiverActivity"
        tools:node="replace">
    <intent-filter>
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE"/>
        <data android:scheme="https"
              android:host="app.example.com"
              android:path="/oauth2redirect"/>
    </intent-filter>
</activity>

HTTPS redirects can be secured by configuring the redirect URI as an app link in Android M and above. We recommend that a fallback page be configured at the same address to forward authorization responses to your app via a custom scheme, for older Android devices.

Handling the authorization response

Upon completion of the authorization flow, the completion Intent provided to performAuthorizationRequest will be triggered. The authorization response is provided to this activity via Intent extra data, which can be extracted using the fromIntent() methods on AuthorizationResponse and AuthorizationException respectively:

public void onCreate(Bundle b) {
  AuthorizationResponse resp = AuthorizationResponse.fromIntent(getIntent());
  AuthorizationException ex = AuthorizationException.fromIntent(getIntent());
  if (resp != null) {
    // authorization completed
  } else {
    // authorization failed, check ex for more details
  }
  // ...
}

The response can be provided to the AuthState instance for easy persistence and further processing:

authState.update(resp, ex);

If the full redirect URI is required in order to extract additional information that AppAuth does not provide, this is also provided to your activity:

public void onCreate(Bundle b) {
  // ...
  Uri redirectUri = getIntent().getData();
  // ...
}

Exchanging the authorization code

Given a successful authorization response carrying an authorization code, a token request can be made to exchange the code for a refresh token:

authService.performTokenRequest(
    resp.createTokenExchangeRequest(),
    new AuthorizationService.TokenResponseCallback() {
      @Override public void onTokenRequestCompleted(
            TokenResponse resp, AuthorizationException ex) {
          if (resp != null) {
            // exchange succeeded
          } else {
            // authorization failed, check ex for more details
          }
        }
    });

The token response can also be used to update an AuthState instance:

authState.update(resp, ex);

Using access tokens

Finally, the retrieved access token can be used to interact with a resource server. This can be done directly, by extracting the access token from a token response. However, in most cases, it is simpler to use the performActionWithFreshTokens utility method provided by AuthState:

authState.performActionWithFreshTokens(service, new AuthStateAction() {
  @Override public void execute(
      String accessToken,
      String idToken,
      AuthorizationException ex) {
    if (ex != null) {
      // negotiation for fresh tokens failed, check ex for more details
      return;
    }

    // use the access token to do something ...
  }
});

This also updates the AuthState object with current access, id, and refresh tokens. If you are storing your AuthState in persistent storage, you should write the updated copy in the callback to this method.

Ending current session

Given you have a logged in session and you want to end it. In that case you need to get:

  • AuthorizationServiceConfiguration
  • valid Open Id Token that you should get after authentication
  • End of session URI that should be provided within you OpenId service config

First you have to build EndSessionRequest

EndSessionRequest endSessionRequest =
    new EndSessionRequest.Builder(authorizationServiceConfiguration)
        .setIdTokenHint(idToken)
        .setPostLogoutRedirectUri(endSessionRedirectUri)
        .build();

This request can then be dispatched using one of two approaches.

a startActivityForResult call using an Intent returned from the AuthorizationService, or by calling performEndSessionRequest and providing pending intent for completion and cancelation handling activities.

The startActivityForResult approach is simpler to use but may require more processing of the result:

private void endSession() {
  AuthorizationService authService = new AuthorizationService(this);
  Intent endSessionItent = authService.getEndSessionRequestIntent(endSessionRequest);
  startActivityForResult(endSessionItent, RC_END_SESSION);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  if (requestCode == RC_END_SESSION) {
    EndSessionResponse resp = EndSessionResponse.fromIntent(data);
    AuthorizationException ex = AuthorizationException.fromIntent(data);
    // ... process the response or exception ...
  } else {
    // ...
  }
}

If instead you wish to directly transition to another activity on completion or cancelation, you can use performEndSessionRequest:

AuthorizationService authService = new AuthorizationService(this);

authService.performEndSessionRequest(
    endSessionRequest,
    PendingIntent.getActivity(this, 0, new Intent(this, MyAuthCompleteActivity.class), 0),
    PendingIntent.getActivity(this, 0, new Intent(this, MyAuthCanceledActivity.class), 0));

End session flow will also work involving browser mechanism that is described in authorization mechanism session. Handling response mechanism with transition to another activity should be as follows:

public void onCreate(Bundle b) {
 EndSessionResponse resp = EndSessionResponse.fromIntent(getIntent());
 AuthorizationException ex = AuthorizationException.fromIntent(getIntent());
 if (resp != null) {
   // authorization completed
 } else {
   // authorization failed, check ex for more details
 }
 // ...
}

AuthState persistence

Instances of AuthState keep track of the authorization and token requests and responses. This is the only object that you need to persist to retain the authorization state of the session. Typically, one would do this by storing the authorization state in SharedPreferences or some other persistent store private to the app:

@NonNull public AuthState readAuthState() {
  SharedPreferences authPrefs = getSharedPreferences("auth", MODE_PRIVATE);
  String stateJson = authPrefs.getString("stateJson", null);
  if (stateJson != null) {
    return AuthState.jsonDeserialize(stateJson);
  } else {
    return new AuthState();
  }
}

public void writeAuthState(@NonNull AuthState state) {
  SharedPreferences authPrefs = getSharedPreferences("auth", MODE_PRIVATE);
  authPrefs.edit()
      .putString("stateJson", state.jsonSerializeString())
      .apply();
}

The demo app has an AuthStateManager type which demonstrates this in more detail.

Advanced configuration

AppAuth provides some advanced configuration options via AppAuthConfiguration instances, which can be provided to AuthorizationService during construction.

Controlling which browser is used for authorization

Some applications require explicit control over which browsers can be used for authorization - for example, to require that Chrome be used for second factor authentication to work, or require that some custom browser is used for authentication in an enterprise environment.

Control over which browsers can be used can be achieved by defining a BrowserMatcher, and supplying this to the builder of AppAuthConfiguration. A BrowserMatcher is suppled with a BrowserDescriptor instance, and must decide whether this browser is permitted for the authorization flow.

By default, AnyBrowserMatcher is used.

For your convenience, utility classes to help define a browser matcher are provided, such as:

  • Browsers: contains a set of constants for the official package names and signatures of Chrome, Firefox and Samsung SBrowser.
  • VersionedBrowserMatcher: will match a browser if it has a matching package name and signature, and a version number within a defined VersionRange. This class also provides some static instances for matching Chrome, Firefox and Samsung SBrowser.
  • BrowserAllowList: takes a list of BrowserMatcher instances, and will match a browser if any of these child BrowserMatcher instances signals a match.
  • BrowserDenyList: the inverse of BrowserAllowList - takes a list of browser matcher instances, and will match a browser if it does not match any of these child BrowserMatcher instances.

For instance, in order to restrict the authorization flow to using Chrome or SBrowser as a custom tab:

AppAuthConfiguration appAuthConfig = new AppAuthConfiguration.Builder()
    .setBrowserMatcher(new BrowserAllowList(
        VersionedBrowserMatcher.CHROME_CUSTOM_TAB,
        VersionedBrowserMatcher.SAMSUNG_CUSTOM_TAB))
    .build();
AuthorizationService authService =
        new AuthorizationService(context, appAuthConfig);

Or, to prevent the use of a buggy version of the custom tabs in Samsung SBrowser:

AppAuthConfiguration appAuthConfig = new AppAuthConfiguration.Builder()
    .setBrowserMatcher(new BrowserDenyList(
        new VersionedBrowserMatcher(
            Browsers.SBrowser.PACKAGE_NAME,
            Browsers.SBrowser.SIGNATURE_SET,
            true, // when this browser is used via a custom tab
            VersionRange.atMost("5.3")
        )))
    .build();
AuthorizationService authService =
        new AuthorizationService(context, appAuthConfig);

Customizing the connection builder for HTTP requests

It can be desirable to customize how HTTP connections are made when performing token requests, for instance to use certificate pinning or to add additional trusted certificate authorities for an enterprise environment. This can be achieved in AppAuth by providing a custom ConnectionBuilder instance.

For example, to custom the SSL socket factory used, one could do the following:

AppAuthConfiguration appAuthConfig = new AppAuthConfiguration.Builder()
    .setConnectionBuilder(new ConnectionBuilder() {
      public HttpURLConnection openConnect(Uri uri) throws IOException {
        URL url = new URL(uri.toString());
        HttpURLConnection connection =
            (HttpURLConnection) url.openConnection();
        if (connection instanceof HttpsUrlConnection) {
          HttpsURLConnection connection = (HttpsURLConnection) connection;
          connection.setSSLSocketFactory(MySocketFactory.getInstance());
        }
      }
    })
    .build();

Issues with ID Token validation

ID Token validation was introduced in 0.8.0 but not all authorization servers or configurations support it correctly.

AppAuthConfiguration appAuthConfig = new AppAuthConfiguration.Builder()
    .setSkipIssuerHttpsCheck(true)
    .build()
  • For services that don't support nonce[s] resulting in IdTokenException Nonce mismatch just set nonce to null on the AuthorizationRequest. Please consider raising an issue with your Identity Provider and removing this once it is fixed.
AuthorizationRequest authRequest = authRequestBuilder
    .setNonce(null)
    .build();

Dynamic client registration

AppAuth supports the OAuth2 dynamic client registration protocol. In order to dynamically register a client, create a RegistrationRequest and dispatch it using performRegistrationRequest on your AuthorizationService instance.

The registration endpoint can either be defined directly as part of your AuthorizationServiceConfiguration, or discovered from an OpenID Connect discovery document.

RegistrationRequest registrationRequest = new RegistrationRequest.Builder(
    serviceConfig,
    Arrays.asList(redirectUri))
    .build();

Requests are dispatched with the help of AuthorizationService. As this request is asynchronous the response is passed to a callback:

service.performRegistrationRequest(
    registrationRequest,
    new AuthorizationService.RegistrationResponseCallback() {
        @Override public void onRegistrationRequestCompleted(
            @Nullable RegistrationResponse resp,
            @Nullable AuthorizationException ex) {
            if (resp != null) {
                // registration succeeded, store the registration response
                AuthState state = new AuthState(resp);
                //proceed to authorization...
            } else {
              // registration failed, check ex for more details
            }
         }
    });

Utilizing client secrets (DANGEROUS)

We strongly recommend you avoid using static client secrets in your native applications whenever possible. Client secrets derived via a dynamic client registration are safe to use, but static client secrets can be easily extracted from your apps and allow others to impersonate your app and steal user data. If client secrets must be used by the OAuth2 provider you are integrating with, we strongly recommend performing the code exchange step on your backend, where the client secret can be kept hidden.

Having said this, in some cases using client secrets is unavoidable. In these cases, a ClientAuthentication instance can be provided to AppAuth when performing a token request. This allows additional parameters (both HTTP headers and request body parameters) to be added to token requests. Two standard implementations of ClientAuthentication are provided:

  • ClientSecretBasic: includes a client ID and client secret as an HTTP Basic Authorization header.
  • ClientSecretPost: includes a client ID and client secret as additional request parameters.

So, in order to send a token request using HTTP basic authorization, one would write:

ClientAuthentication clientAuth = new ClientSecretBasic(MY_CLIENT_SECRET);
TokenRequest req = ...;
authService.performTokenRequest(req, clientAuth, callback);

This can also be done when using performActionWithFreshTokens on AuthState:

ClientAuthentication clientAuth = new ClientSecretPost(MY_CLIENT_SECRET);
authState.performActionWithFreshTokens(
    authService,
    clientAuth,
    action);

Modifying or contributing to AppAuth

This project requires the Android SDK for API level 25 (Nougat) to build, though the produced binaries only require API level 16 (Jellybean) to be used. We recommend that you fork and/or clone this repository to make modifications; downloading the source has been known to cause some developers problems.

For contributors, see the additional instructions in CONTRIBUTING.md.

Building from the Command line

AppAuth for Android uses Gradle as its build system. In order to build the library and app binaries, run ./gradlew assemble. The library AAR files are output to library/build/outputs/aar, while the demo app is output to app/build/outputs/apk. In order to run the tests and code analysis, run ./gradlew check.

Building from Android Studio

In AndroidStudio, File -> New -> Import project. Select the root folder (the one with the build.gradle file).

appauth-android's People

Contributors

3flex avatar agologan avatar arvindsinghtomar avatar axelnennker avatar bbqsrc avatar blundell avatar codydunlap avatar dkiro avatar hnljp avatar iainmcgin avatar jbj88817 avatar keithcasey-okta avatar madurangasiriwardena avatar malikde avatar mark-kowalski avatar mdietrichstein avatar mensly avatar mlegy avatar ntnhon avatar oyvindrobertsen avatar panzerfahrer avatar rojoiii avatar subside avatar sudonatalie avatar tikurahul avatar trebouillon avatar vickychijwani avatar wdawson avatar williamdenniss avatar witrisna avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

appauth-android's Issues

Webview possibility

Hi!
When the user doesn't have the Chrome app the Custom Tab will open the browser by default making the application UI ugly in my opinion. Is there a chance that I can open the login url on a webview? I know that the instructions says no but I still want to know if there's a chance.

Verify that space-delimited lists are ASCII-only

OAuth2 and OpenID Connect make use of space-delimited string lists in a number of parameters (e.g. scope, prompt). While we currently check that these strings are null or not empty, we are not checking that they are ASCII-only.

ROOT/config is a directory

Hi,
I'm imported the project into Android Studio and it doesn't build.
It complains at line 26:
def grgit = org.ajoberstar.grgit.Grgit.open(dir: '.')
What should I do?

Moreover, for every Gradle command, lint is saying "cannot resolve symbol"

Support "prompt" OpenID Connect auth request parameter

OpenID Connect Core 1.0, Section 3.1.2.1,
"prompt" parameter:

OPTIONAL. Space delimited, case sensitive list of ASCII string values that specifies whether the Authorization Server prompts the End-User for reauthentication and consent. The defined values are:

none
The Authorization Server MUST NOT display any authentication or consent user interface pages. An error is returned if an End-User is not already authenticated or the Client does not have pre-configured consent for the requested Claims or does not fulfill other conditions for processing the request. The error code will typically be login_required, interaction_required, or another code defined in Section 3.1.2.6. This can be used as a method to check for existing authentication and/or consent.

login
The Authorization Server SHOULD prompt the End-User for reauthentication. If it cannot reauthenticate the End-User, it MUST return an error, typically login_required.

consent
The Authorization Server SHOULD prompt the End-User for consent before returning information to the Client. If it cannot obtain consent, it MUST return an error, typically consent_required.

select_account
The Authorization Server SHOULD prompt the End-User to select a user account. This enables an End-User who has multiple accounts at the Authorization Server to select amongst the multiple accounts that they might have current sessions for. If it cannot obtain an account selection choice made by the End-User, it MUST return an error, typically account_selection_required.

Support "nonce" OpenID Connect auth request parameter

OPTIONAL. String value used to associate a Client session with an ID Token, and to mitigate replay attacks. The value is passed through unmodified from the Authentication Request to the ID Token. Sufficient entropy MUST be present in the nonce values used to prevent attackers from guessing values. For implementation notes, see Section 15.5.2.

Section 3.2.2.1, "Authentication Request" specifies that this parameter is REQUIRED for implicit flow authentication requests (those with response type id_token or id_token token).

java.io.FileNotFoundExcetion from library code

I am trying to do openID connect Auth, successfully got AuthorizationResponse, but getting:

AuthorizationException: {"type":0,"code":3,"errorDescription":"Network error"}
caused by

java.io.FileNotFoundException: http://api.my_addr.com:8080/connect/token

on line #154 of AuthorizationService.class
is = conn.getInputStream();
I found answer on stackoverflow (http://stackoverflow.com/questions/9365829/filenotfoundexception-for-httpurlconnection-in-ice-cream-sandwich), but I cannot edit library code.

Support "claims" OpenID Connect auth request parameter

OpenID Connect Core 1.0, Section 5.5, claims parameter:

OPTIONAL. This parameter is used to request that specific Claims be returned. The value is a JSON object listing the requested Claims.

An example Claims request is as follows:

{
  "userinfo":
  {
      "given_name": {"essential": true},
      "nickname": null,
      "email": {"essential": true},
      "email_verified": {"essential": true},
      "picture": null,
      "http://example.info/claims/groups": null
  },
  "id_token":
  {
      "auth_time": {"essential": true},
      "acr": {"values": ["urn:mace:incommon:iap:silver"] }
  }
}

Clarification of instructions for "Configuring Google Sign In"

I followed the instructions in app/README.md for configuring the demo app included in this repository with Google Sign-In and found some things that maybe could be made clearer:

  • The enabling Google services page for Google Sign-In expects the SHA1 fingerprint of the certificate, while the command shown for extracting fingerprints picks SHA256 (via grep SHA256). The command from e.g. https://support.google.com/cloud/answer/6158849?hl=en#android should be shown/linked instead.
  • There is no mention of having to change the value of google_enabled in res/values/idp_configs.xml to true (kind of obvious, but I missed it the first time I edited the file).

Support "acr_values" OpenID Connect auth request parameter

OpenID Connect Core 1.0, Section 3.1.2.1, acr_values parameter:

OPTIONAL. Requested Authentication Context Class Reference values. Space-separated string that specifies the acr values that the Authorization Server is being requested to use for processing this Authentication Request, with the values appearing in order of preference. The Authentication Context Class satisfied by the authentication performed is returned as the acr Claim Value, as specified in Section 2. The acr Claim is requested as a Voluntary Claim by this parameter.

Support "id_token_hint" OpenID Connect auth request parameter

OpenID Connect Core 1.0, Section 3.1.2.1, id_token_hint parameter:

OPTIONAL. ID Token previously issued by the Authorization Server being passed as a hint about the End-User's current or past authenticated session with the Client. If the End-User identified by the ID Token is logged in or is logged in by the request, then the Authorization Server returns a positive response; otherwise, it SHOULD return an error, such as login_required. When possible, an id_token_hint SHOULD be present when prompt=none is used and an invalid_request error MAY be returned if it is not; however, the server SHOULD respond successfully when possible, even if it is not present. The Authorization Server need not be listed as an audience of the ID Token when it is used as an id_token_hint value.

If the ID Token received by the RP from the OP is encrypted, to use it as an id_token_hint, the Client MUST decrypt the signed ID Token contained within the encrypted ID Token. The Client MAY re-encrypt the signed ID token to the Authentication Server using a key that enables the server to decrypt the ID Token, and use the re-encrypted ID token as the id_token_hint value.

Can't get TokenActivity to work with custom IdP

Hello all,
I've downloaded and run locally mitreid server, and I'm trying to integrate it with this app, following the instructions, but I have issues with TokenActivity, to be more specific I get the snackbar saying:
"Token refresh Failed"
and debugging I found out that there is an "AuthorizationException" whose json is:
{"type":0,"code":3,"errorDescription":"Network error"}

The issue is on token request (I debugged it), because i can see the login and the redirect works correctly (I see the app going on the web server apps list), but token won't work.
I don't think it is an issue of a mitre configuration (but it could be?),

The grant type is set to: "authorization code" and "refresh", under response type, "code" "token" and "id_token" are enabled.

What else can I do? the token endpoint is correct, but is not working!

Missing port when constructing URL for token request

In AuthorizationService::TokenRequestTask::doInBackground(), the URL that is constructed (local variable 'url' inside try block) is missing the port from the request URI. This omission seems to cause FileNotFoundException when connecting to the token endpoint.

UnsupportedOperationException: This isn't a hierarchical URI

Hi,

I'm trying to get this library to work with the Microsoft v2 Authentication Endpoint. I'm running into the following error:

java.lang.RuntimeException: Unable to start activity ComponentInfo{net.openid.appauthdemo/net.openid.appauth.RedirectUriReceiverActivity}: java.lang.UnsupportedOperationException: This isn't a hierarchical URI.

The exception is thrown in RedirectUriReceiverActivity.

The problem is that the Microsoft URI format is like this urn:ietf:wg:oauth:2.0:oob and Uri.getQueryParameter doesn't like it.

Is there any workaround to make it work with this provider?
Thanks for the great work, by the way.

Support "display" OpenID Connect auth request parameter

OpenID Connect Core 1.0, Section 3.1.2.1, "display" parameter:

OPTIONAL. ASCII string value that specifies how the Authorization Server displays the authentication and consent user interface pages to the End-User. The defined values are:

page
The Authorization Server SHOULD display the authentication and consent UI consistent with a full User Agent page view. If the display parameter is not specified, this is the default display mode.

popup
The Authorization Server SHOULD display the authentication and consent UI consistent with a popup User Agent window. The popup User Agent window should be of an appropriate size for a login-focused dialog and should not obscure the entire window that it is popping up over.

touch
The Authorization Server SHOULD display the authentication and consent UI consistent with a device that leverages a touch interface.

wap
The Authorization Server SHOULD display the authentication and consent UI consistent with a "feature phone" type display.

Logout support

Hello.

I managed to include Keycloak logout support in my company. What I did was copy the following files:

  • AuthorizationService => LogoutService
  • AuthorizationRequest => LogoutRequest
  • RedirectUriReceiverActivity => LogoutUriReceiverActivity
  • PendingIntentStore => PendingLogoutIntentStore

So basically I used the same CustomTab/Browser authorization mechanism but for logout. I'm aware open id connect logout is still a draft. However it seemed to me that the code is not generic enough to allow "CustomTab commands" other than login.

What I want to know is if this is correct and if it's not what can I do to avoid copy/paste?

Google auth example not working

I'm trying to get the demo project working to authenticate me with Google. I have two problems:

  1. The AppAuth-Android framework isn't sending the required client_secret parameter when it makes its token request. I can fix this from the client side by passing in an additional_parameters map.
  2. The response from Google includes an id_token but not a refresh_token. Then when I press the "View user info" button that appears on TokenActivity it crashes because of an invalid state exception - it doesn't have the necessary refresh token

Any ideas?

Android for Work behavior when no browser is available in the work profile

Need to validate what happens with AppAuth if you try and do an authorization request in a Work profile without a browser being present in the Work profile.

Generally speaking if you fire a browser intent in a Work profile with no browser, it will open the personal browser, and the OAuth flow will not complete successfully (intents from work->personal are one-way and can't be returned).

Ideally we would catch this condition before firing any intents.

Can't build with Android Studio

It seems gradle can't find "org.ajoberstar.grgit.Grgit" ?

Error:(132, 0) Cause: repository not found: /Users/cwang/Downloads/AppAuth-Android-master
Open File

Authentication state lost while app switching

Hello,
we have integrated this into an openid connect solution without issue really top notch, however we have run into a problem. In essence, our app requires to switch to SMS to get a one time password, during authentication. When we do, we lose the application state and we no longer see the screen for the OTP in the custom tab.

Is there a way to force the auth state to be stored and restored upon coming back?

Support JWT decoding and validation

Support validating JWTs and extracting their claims as a map. This will require the ability to either dynamically use the jwks_uri keys provided by the provider's discovery document, or a set of acceptable keys provided by the developer.

Can the code handle app process death while the user is in a web browser in front?

I should preface this by stating that I'm only looking at this library as sample code (for the "modernized" OAUTH flow), and have no plans to actually use it.

It's not apparent to me if the sample app and the library would be able to handle this scenario:

1 - User initiates approval flow (the initial stage where "this app would like to know your email, name, ...")

2 - The app makes a PendingIntent for TokenActivity and the library stores it in PendingIntentStore

3 - Chome (or some other browser) comes up in front of the app, user starts interacting with it

4 - Android kills the app which is now in background (memory pressure, whatever). PendingIntentStore and everything in it is gone

5 - User finally presses "Agree", the browser fires the "custom scheme intent"

6 - Android creates a new process for the app and launches RedirectUriReceiverActivity

And now:

7 - PendingIntentStore will not have the original intent and getOriginalRequest inside RedirectUriReceiverActivity will return null and the subsequent logic for handling the authorization code will fail.

That is, unless I'm missing something and MainActivity (which was in front when Chrome with the authorization screen popped up) or something else is going to re-create the original pending intent and register it in PendingIntentStore before RedirectUriReceiverActivity needs to use it.

Another interesting case is the user changing the device's orientation while on the approval screen in the web browser. Then Android may destroy and re-create MainActivity and that, from looking at the code, will launch a whole new "flow" for approval.

Activity net.openid.appauthdemo.MainActivity has leaked ServiceConnection

I've encountered a leak problem. I was using a simulator without Chrome installed. On a Chrome installed simulator the leak won't happen.

This leak was occurred when I hit the back button and while the MainActivity turned into background.

And when I dig a little deeper into the problem I found that the mBrowserPackage in the BrowserHandler was resolved to com.android.browser and the return value of CustomTabsClient#bindCustomTabsService(...) is false. So the connection is not retained in the BrowserHandler and when BrowserHandler#unbind() is called, nothing is unbind.

I've tried to retained the connection even when CustomTabsClient#bindCustomTabsService(...) returns false. It seems solve the leak problem, but I'm not sure will there be any side effects.

07-06 10:12:45.442 3929-3929/net.openid.appauthdemo E/ActivityThread: Activity net.openid.appauthdemo.MainActivity has leaked ServiceConnection net.openid.appauth.BrowserHandler$1@2206f76 that was originally bound here
                                                                      android.app.ServiceConnectionLeaked: Activity net.openid.appauthdemo.MainActivity has leaked ServiceConnection net.openid.appauth.BrowserHandler$1@2206f76 that was originally bound here
                                                                          at android.app.LoadedApk$ServiceDispatcher.<init>(LoadedApk.java:1092)
                                                                          at android.app.LoadedApk.getServiceDispatcher(LoadedApk.java:986)
                                                                          at android.app.ContextImpl.bindServiceCommon(ContextImpl.java:1303)
                                                                          at android.app.ContextImpl.bindService(ContextImpl.java:1286)
                                                                          at android.content.ContextWrapper.bindService(ContextWrapper.java:604)
                                                                          at android.support.customtabs.CustomTabsClient.bindCustomTabsService(CustomTabsClient.java:60)
                                                                          at net.openid.appauth.BrowserHandler.bindCustomTabsService(BrowserHandler.java:86)
                                                                          at net.openid.appauth.BrowserHandler.<init>(BrowserHandler.java:61)
                                                                          at net.openid.appauth.AuthorizationService.<init>(AuthorizationService.java:101)
                                                                          at net.openid.appauthdemo.MainActivity.onCreate(MainActivity.java:63)
                                                                          at android.app.Activity.performCreate(Activity.java:6237)
                                                                          at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1107)
                                                                          at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2369)
                                                                          at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476)
                                                                          at android.app.ActivityThread.-wrap11(ActivityThread.java)
                                                                          at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344)
                                                                          at android.os.Handler.dispatchMessage(Handler.java:102)
                                                                          at android.os.Looper.loop(Looper.java:148)
                                                                          at android.app.ActivityThread.main(ActivityThread.java:5417)
                                                                          at java.lang.reflect.Method.invoke(Native Method)
                                                                          at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                                                                          at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

Library difficult to use/integrate

Apologize for the generic issue but would love to see the library and sample like other android specific libraries from google open source like google-services/signin
or developer relations library like easypermissions (for non-official ones)

The talk and the premises addressed here are bleeding edge (custom tabs, PKCE etc.) but setting it up is a huge pain in the ass.

Is adding this in firebase authentication on the roadmap?

Support "registration" OpenID Connect auth request parameter

OpenID Connect Core 1.0, Section 7.2.1, registration parameter:

OPTIONAL. This parameter is used by the Client to provide information about itself to a Self-Issued OP that would normally be provided to an OP during Dynamic Client Registration. The value is a JSON object containing Client metadata values, as defined in Section 2.1 of the OpenID Connect Dynamic Client Registration 1.0 specification. The registration parameter SHOULD NOT be used when the OP is not a Self-Issued OP.

Error when no default browser is defined

I've found what seems to be an error on the BrowserPackageHelper when no default browser is defined.
I get a java.lang.NullPointerException on the isFullBrowser method because the ResolveInfo is not null however the ResolveInfo.filter is null.
This seems to be the documented behaviour for the PackageManager.resolveActivity method when more than one option exists (i.e. no default)- "If multiple matching activities are found and there is no default set, returns a ResolveInfo containing something else, such as the activity resolver."

Adding additional IDP

I'm trying to get the demo app up and running against an IDP/OP in my domain (so not Google Sign-In).
How do I specify the redirect URI in idp_configs.xml/idp_configs_optional.xml, such that the authentication response is delivered through an Intent (similar to the case for Google Sign-In)?
The example documentation only shows an HTTP-url as redirect_uri, which I think assumes another web server receiving the response and not the demo app?
Do I need to add another intent filter in the manifest, similar to

<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="@string/google_auth_redirect_scheme"/>
</intent-filter>
?

No browser found exception

If there is no browser at all user would get unexpected ActivityNotFoundException:

android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.VIEW dat=https://accounts.google.com/o/oauth2/v2/auth?redirect_uri=com.googleusercontent.apps.nope:/oauth2redirect&client_id=529788761403-aj9ff2svir6lsqjnck6jnjmqhfop77mk.apps.googleusercontent.com&response_type=code&state=Od4cn-XBtdi3Qdjnl5t7wg&scope=openid profile email&code_challenge=WJOmc7Hs3GcF0FpQ7dUJ3ISZxwETpkSmBods2dgMLr4&code_challenge_method=S256 flg=0x40000000 pkg=com.android.chrome (has extras) }
at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:1551)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1422)
at android.app.Activity.startActivityForResult(Activity.java:3375)
at android.app.Activity.startActivityForResult(Activity.java:3331)
at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:843)
at android.app.Activity.startActivity(Activity.java:3546)
at android.app.Activity.startActivity(Activity.java:3514)
at net.openid.appauth.AuthorizationService.performAuthorizationRequest(AuthorizationService.java:173)
at net.openid.appauthdemo.MainActivity.makeAuthRequest(MainActivity.java:143)
at net.openid.appauthdemo.MainActivity.access$100(MainActivity.java:54)
at net.openid.appauthdemo.MainActivity$1.onFetchConfigurationCompleted(MainActivity.java:89)
at net.openid.appauth.AuthorizationServiceConfiguration$ConfigurationRetrievalAsyncTask.onPostExecute(AuthorizationServiceConfiguration.java:325)
at net.openid.appauth.AuthorizationServiceConfiguration$ConfigurationRetrievalAsyncTask.onPostExecute(AuthorizationServiceConfiguration.java:266)
at android.os.AsyncTask.finish(AsyncTask.java:631)
at android.os.AsyncTask.access$600(AsyncTask.java:177)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:644)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:153)
at android.app.ActivityThread.main(ActivityThread.java:5022)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1032)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:790)
at dalvik.system.NativeStart.main(Native Method)

Losing ID token during token refresh

Consider a scenario where the client requests an ID token, access token and refresh token on their authorization request. This is granted, and after exchange of the authorization code, they receive the requested tokens. An IDP can freely choose different expiration times for access tokens and ID tokens, so let's say that an ID token lasts 24 hours, and an access token lasts 1 hour.

After an hour of using the tokens with the help of AuthState.performActionWithFreshTokens, the access token expires and we request fresh tokens. The received token response replaces the original token response, which contained the (still valid) ID token. This response may or may not contain a new ID token; this is entirely up to the behavior of the IDP - I can't see anything in the spec that would require a refresh exchange to always yield the same tokens as the original code exchange.

So, the original ID token is lost when the token response is replaced, and there may not be a new one in the new token response to replace it. Should AuthState be storing token values independently of the authorization and token responses, to avoid this scenario?

Authorization Code retrieval from redirect URI

I'm trying to use the library in conjunction with a 3rd party OAuth provider. I'm having issues though trying to return the Authorization Code back from the RedirectUriReceiverActivity. I have 2 options the server provides: either use http://localhost as the redirect URI and the Authorization Code will be returned as a query parameter; or use urn:ietf:wg:oauth:2.0:oob as the redirect URI and receive the code inside the web browser title.

Now I have 2 questions:

  1. Are you going to add support for retrieving the Authorization Code from the browser title bar? I suppose not, but thought of asking anyway.
  2. When the OAuth server redirects the client to http://localhost, the browser obviously says "This site can't be reached", but the authorization service doesn't return the code to the calling app, it just keeps displaying the browser window. I've already added the URI scheme as shown in the sample code below. What seems to be the problem in this case?

Calling the authorization service:

Uri authUri = Uri.parse(URI_AUTH);
Uri redirectUri = Uri.parse("http://localhost");
Uri tokenUri = Uri.parse(URI_TOKEN);
AuthorizationServiceConfiguration authorizationServiceConfiguration =
        new AuthorizationServiceConfiguration(authUri, tokenUri);
AuthorizationRequest authorizationRequest = new AuthorizationRequest.Builder(
        authorizationServiceConfiguration,
        CLIENT_ID,
        AuthorizationRequest.RESPONSE_TYPE_CODE,
        redirectUri)
        .build();
AuthorizationService service = new AuthorizationService(context);
Intent postAuthIntent = new Intent(context, SignInActivity.class);
service.performAuthorizationRequest(
        authorizationRequest,
        PendingIntent.getActivity(context, authorizationRequest.hashCode(), postAuthIntent, 0));

URI scheme:

<activity android:name="net.openid.appauth.RedirectUriReceiverActivity">
      <intent-filter>
           <action android:name="android.intent.action.VIEW"/>
           <category android:name="android.intent.category.DEFAULT"/>
           <category android:name="android.intent.category.BROWSABLE"/>
           <data android:scheme="localhost"/>
      </intent-filter>
</activity>

Activity didn't handle redirect.

I'llconfigure my Activity Manifest with net.openid.appauth.RedirectUriReceiverActivity according with guide. But after authorization is complete and callback page is shown in system browser onCreate isn't called. And there is no possibility to check data recevied from oAuth. Can it be caused by such error - "E/Surface: getSlotFromBufferLocked: unknown buffer: 0xa214d070" which i see in logs after auth initial request is perfromed? Thank you

Support "ui_locales" OpenID Connect auth request parameter

OpenID Connect Core 1.0, Section 3.1.2.1, ui_locales parameter:

OPTIONAL. End-User's preferred languages and scripts for the user interface, represented as a space-separated list of BCP47 [RFC5646] language tag values, ordered by preference. For instance, the value "fr-CA fr en" represents a preference for French as spoken in Canada, then French (without a region designation), followed by English (without a region designation). An error SHOULD NOT result if some or all of the requested locales are not supported by the OpenID Provider.

Support sending auth requests as a JWT using the "request" parameter

OpenID Connect Core 1.0, Section 6.0, request parameter:

OPTIONAL. This parameter enables OpenID Connect requests to be passed in a single, self-contained parameter and to be optionally signed and/or encrypted. The parameter value is a Request Object value, as specified in Section 6.1. It represents the request as a JWT whose Claims are the request parameters.

It will likely be preferable to support this as a flag on the authorization request object rather than a parameter in the conventional way.

Support "login_hint" OpenID Connect auth request parameter

OpenID Connect Core 1.0, Section 3.1.2.1, login_hint parameter:

OPTIONAL. Hint to the Authorization Server about the login identifier the End-User might use to log in (if necessary). This hint can be used by an RP if it first asks the End-User for their e-mail address (or other identifier) and then wants to pass that value as a hint to the discovered authorization service. It is RECOMMENDED that the hint value match the value used for discovery. This value MAY also be a phone number in the format specified for the phone_number Claim. The use of this parameter is left to the OP's discretion.

Support "max_age" OpenID Connect auth request parameter

OpenID Connect Core 1.0, Section 3.1.2.1, max_age parameter:

OPTIONAL. Maximum Authentication Age. Specifies the allowable elapsed time in seconds since the last time the End-User was actively authenticated by the OP. If the elapsed time is greater than this value, the OP MUST attempt to actively re-authenticate the End-User. (The max_age request parameter corresponds to the OpenID 2.0 PAPE max_auth_age request parameter). When max_age is used, the ID Token returned MUST include an auth_time Claim Value.

Detecting user initiated closing of the authorization flow.

Is there a way to determine if the user closed the authorization flow, either via the close button on the chrome custom tab, or by hitting the android back button.

I'm trying to implement a re-auth flow in case of token refresh failure. If that happens, i'd like the user to reauth and if successful stay where they are. If they close it themselves, i want to just close all the open activities on the app.

New version of the library

Just to gather some changes; when the dynamic client registration is completed (as of #61), is this library ready for v0.3.0 soon?
PR #66 and #67 are contingent on a new version (and should not be merged until that is released).
In connection with a new version, the javadoc hosted at the github pages will also need to be re-generated.

Implicit grant type supported?

Question:
As far as I can see from the documentation, the implicit grant type for OAuth 2.0 does not seem to be supported yet. Is my understanding correct?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.